public DefaultApkSignerEngine(X509Certificate2 certificate, int minSdkVersion, bool v1SigningEnabled,
                                      bool v2SigningEnabled, DigestAlgorithm digestAlgorithm)
        {
            _v1SigningEnabled   = v1SigningEnabled;
            _v2SigningEnabled   = v2SigningEnabled;
            _v1SignaturePending = v1SigningEnabled;
            _v2SignaturePending = v2SigningEnabled;

            if (v1SigningEnabled)
            {
                var v1SignerName = V1SchemeSigner.GetSafeSignerName(certificate.FriendlyName);
                // Check whether the signer's name is unique among all v1 signers
                var v1SignatureDigestAlgorithm = digestAlgorithm ??
                                                 V1SchemeSigner.GetSuggestedSignatureDigestAlgorithm(
                    certificate.PublicKey, minSdkVersion);
                var v1SignerConfig = new V1SchemeSigner.SignerConfig();
                v1SignerConfig.Name        = v1SignerName;
                v1SignerConfig.Certificate = certificate;
                v1SignerConfig.SignatureDigestAlgorithm = v1SignatureDigestAlgorithm;

                _v1SignerConfigs                      = v1SignerConfig;
                _v1ContentDigestAlgorithm             = v1SignatureDigestAlgorithm;
                _signatureExpectedOutputJarEntryNames = V1SchemeSigner.GetOutputEntryNames(_v1SignerConfigs, minSdkVersion);

                _v1ContentDigestAlgorithm = V1SchemeSigner.GetSuggestedSignatureDigestAlgorithm(certificate.PublicKey, minSdkVersion);
            }

            if (v2SigningEnabled)
            {
                var v2SignerConfig = new V2SchemeSigner.SignerConfig();
                v2SignerConfig.Certificates       = certificate;
                v2SignerConfig.SignatureAlgorithm = V2SchemeSigner.GetSuggestedSignatureAlgorithms(certificate.PublicKey, minSdkVersion, digestAlgorithm);
                _v2SignerConfigs = v2SignerConfig;
            }
        }
 /// <summary>
 /// Returns the output policy for the provided input JAR entry.
 /// </summary>
 /// <param name="entryName"></param>
 /// <returns></returns>
 private OutputPolicy GetInputJarEntryOutputPolicy(string entryName)
 {
     if (_signatureExpectedOutputJarEntryNames.Contains(entryName))
     {
         return(OutputPolicy.OutputByEngine);
     }
     if (V1SchemeSigner.IsJarEntryDigestNeededInManifest(entryName))
     {
         return(OutputPolicy.Output);
     }
     return(OutputPolicy.Skip);
 }
        /// <summary>
        /// Indicates to this engine that the specified JAR entry was output.
        ///
        /// It is unnecessary to invoke this method for entries added to output by this engine (e.g.,
        /// requested by <see cref="OutputJarEntries"/> provided the entries were output with exactly the
        /// data requested by the engine.
        /// </summary>
        /// <param name="entryName"></param>
        /// <returns>
        /// request to inspect the entry or <code>null</code> if the engine does not need to inspect
        /// the entry.The request must be fulfilled before<see cref="OutputJarEntries"/> is invoked.
        /// </returns>
        public IInspectJarEntryRequest OutputJarEntry(string entryName)
        {
            InvalidateV2Signature();

            if (!_v1SigningEnabled)
            {
                // No need to inspect JAR entries when v1 signing is not enabled.
                return(null);
            }


            // v1 signing is enabled
            if (V1SchemeSigner.IsJarEntryDigestNeededInManifest(entryName))
            {
                // This entry is covered by v1 signature. We thus need to inspect the entry's data to
                // compute its digest(s) for v1 signature.
                // TODO: Handle the case where other signer's v1 signatures are present and need to be
                // preserved. In that scenario we can't modify MANIFEST.MF and add/remove JAR entries
                // covered by v1 signature.
                InvalidateV1Signature();
                var dataDigestRequest = new GetJarEntryDataDigestRequest(entryName, _v1ContentDigestAlgorithm);
                _outputJarEntryDigestRequests.Add(entryName, dataDigestRequest);
                _outputJarEntryDigests.Remove(entryName);
                return(dataDigestRequest);
            }
            if (_signatureExpectedOutputJarEntryNames.Contains(entryName))
            {
                // This entry is part of v1 signature generated by this engine. We need to check whether
                // the entry's data is as output by the engine.
                InvalidateV1Signature();
                GetJarEntryDataRequest dataRequest;
                if (V1SchemeSigner.ManifestEntryName.Equals(entryName))
                {
                    dataRequest = new GetJarEntryDataRequest(entryName);
                    _inputJarManifestEntryDataRequest = dataRequest;
                }
                else
                {
                    // If this entry is part of v1 signature which has been emitted by this engine,
                    // check whether the output entry's data matches what the engine emitted.
                    dataRequest = (_emittedSignatureJarEntryData.ContainsKey(entryName))
                                    ? new GetJarEntryDataRequest(entryName) : null;
                }
                if (dataRequest != null)
                {
                    _outputSignatureJarEntryDataRequests.Add(entryName, dataRequest);
                }
                return(dataRequest);
            }
            // This entry is not covered by v1 signature and isn't part of v1 signature.
            return(null);
        }
        /// <summary>
        /// Indicates to this engine that all JAR entries have been output.
        /// </summary>
        /// <returns>
        /// request to add JAR signature to the output or <code>null</code> if there is no need to add
        /// a JAR signature.The request will contain additional JAR entries to be output.The
        /// request must be fulfilled before <see cref="OutputZipSections"/> is invoked.
        /// </returns>
        public IOutputJarSignatureRequest OutputJarEntries()
        {
            if (!_v1SignaturePending)
            {
                return(null);
            }
            if ((_inputJarManifestEntryDataRequest != null) &&
                (!_inputJarManifestEntryDataRequest.IsDone))
            {
                throw new InvalidOperationException(
                          "Still waiting to inspect input APK's "
                          + _inputJarManifestEntryDataRequest.EntryName);
            }
            foreach (var digestRequest in _outputJarEntryDigestRequests.Values)
            {
                var entryName = digestRequest.EntryName;
                if (!digestRequest.IsDone)
                {
                    throw new InvalidOperationException(
                              "Still waiting to inspect output APK's " + entryName);
                }
                _outputJarEntryDigests.Add(entryName, digestRequest.Digest);
            }
            _outputJarEntryDigestRequests.Clear();
            foreach (var dataRequest in _outputSignatureJarEntryDataRequests.Values)
            {
                if (!dataRequest.IsDone)
                {
                    throw new InvalidOperationException(
                              "Still waiting to inspect output APK's " + dataRequest.EntryName);
                }
            }

            IList <int> apkSigningSchemeIds = new List <int>();

            if (_v2SigningEnabled)
            {
                apkSigningSchemeIds.Add(2);
            }
            var inputJarManifest =
                (_inputJarManifestEntryDataRequest != null)
                        ? _inputJarManifestEntryDataRequest.Data : null;
            // Check whether the most recently used signature (if present) is still fine.
            List <Tuple <string, byte[]> > signatureZipEntries;

            if ((_addV1SignatureRequest == null) || (!_addV1SignatureRequest.IsDone))
            {
                try
                {
                    signatureZipEntries =
                        V1SchemeSigner.Sign(
                            _v1SignerConfigs,
                            _v1ContentDigestAlgorithm,
                            _outputJarEntryDigests,
                            apkSigningSchemeIds,
                            inputJarManifest);
                }
                catch (CryptographicException e)
                {
                    throw new CryptographicException("Failed to generate v1 signature", e);
                }
            }
            else
            {
                var newManifest = V1SchemeSigner.GenerateManifestFile(_v1ContentDigestAlgorithm, _outputJarEntryDigests, inputJarManifest);
                var emittedSignatureManifest = _emittedSignatureJarEntryData[V1SchemeSigner.ManifestEntryName];
                if (!newManifest.Contents.SequenceEqual(emittedSignatureManifest))
                {
                    // Emitted v1 signature is no longer valid.
                    try
                    {
                        signatureZipEntries =
                            V1SchemeSigner.SignManifest(
                                _v1SignerConfigs,
                                _v1ContentDigestAlgorithm,
                                apkSigningSchemeIds,
                                newManifest);
                    }
                    catch (CryptographicException e)
                    {
                        throw new CryptographicException("Failed to generate v1 signature", e);
                    }
                }
                else
                {
                    // Emitted v1 signature is still valid. Check whether the signature is there in the
                    // output.
                    signatureZipEntries = new List <Tuple <string, byte[]> >();
                    foreach (var expectedOutputEntry in _emittedSignatureJarEntryData)
                    {
                        var entryName         = expectedOutputEntry.Key;
                        var expectedData      = expectedOutputEntry.Value;
                        var actualDataRequest =
                            _outputSignatureJarEntryDataRequests[entryName];
                        if (actualDataRequest == null)
                        {
                            // This signature entry hasn't been output.
                            signatureZipEntries.Add(Tuple.Create(entryName, expectedData));
                            continue;
                        }
                        var actualData = actualDataRequest.Data;
                        if (!expectedData.SequenceEqual(actualData))
                        {
                            signatureZipEntries.Add(Tuple.Create(entryName, expectedData));
                        }
                    }
                    if (signatureZipEntries.Count == 0)
                    {
                        // v1 signature in the output is valid
                        return(null);
                    }
                    // v1 signature in the output is not valid.
                }
            }
            if (signatureZipEntries.Count == 0)
            {
                // v1 signature in the output is valid
                _v1SignaturePending = false;
                return(null);
            }
            var sigEntries = new List <JarEntry>(signatureZipEntries.Count());

            foreach (var entry in signatureZipEntries)
            {
                var entryName = entry.Item1;
                var entryData = entry.Item2;
                sigEntries.Add(new JarEntry(entryName, entryData));
                _emittedSignatureJarEntryData.Add(entryName, entryData);
            }
            _addV1SignatureRequest = new OutputJarSignatureRequestImpl(sigEntries);
            return(_addV1SignatureRequest);
        }