/// <summary>
        /// Signs the provided APK using JAR signing (aka v1 signature scheme) and returns the list of
        /// JAR entries which need to be added to the APK as part of the signature.
        /// </summary>
        /// <param name="signerConfig"></param>
        /// <param name="digestAlgorithm"></param>
        /// <param name="apkSigningSchemeIds"></param>
        /// <param name="manifest"></param>
        /// <returns></returns>
        public static List <Tuple <string, byte[]> > SignManifest(SignerConfig signerConfig,
                                                                  DigestAlgorithm digestAlgorithm, IList <int> apkSigningSchemeIds, OutputManifestFile manifest)
        {
            // For each signer output .SF and .(RSA|DSA|EC) file, then output MANIFEST.MF.
            var signatureJarEntries =
                new List <Tuple <string, byte[]> >(2 * 1 + 1);
            var sfBytes        = GenerateSignatureFile(apkSigningSchemeIds, digestAlgorithm, manifest);
            var signerName     = signerConfig.Name;
            var signatureBlock = GenerateSignatureBlock(signerConfig, sfBytes);

            signatureJarEntries.Add(Tuple.Create("META-INF/" + signerName + ".SF", sfBytes));

            var publicKey = signerConfig.Certificate.PublicKey;
            var signatureBlockFileName =
                "META-INF/" + signerName + ".";

            if (publicKey.Key is RSACryptoServiceProvider)
            {
                signatureBlockFileName += "RSA";
            }
            else if (publicKey.Key is DSACryptoServiceProvider)
            {
                signatureBlockFileName += "DSA";
            }

            signatureJarEntries.Add(
                Tuple.Create(signatureBlockFileName, signatureBlock));
            signatureJarEntries.Add(Tuple.Create(ManifestEntryName, manifest.Contents));
            return(signatureJarEntries);
        }
        private static byte[] GenerateSignatureFile(IList <int> apkSignatureSchemeIds,
                                                    DigestAlgorithm manifestDigestAlgorithm, OutputManifestFile manifest)
        {
            var sf        = new Manifest.Manifest();
            var mainAttrs = sf.MainAttributes;

            mainAttrs.Add(Manifest.Manifest.SignatureVersion, AttributeValueSignatureVersion);
            mainAttrs.Add(AttributeNameCreatedBy, "1.0 (Android)");
            //mainAttrs.Add(ATTRIBUTE_NAME_CREATED_BY, createdBy);
            if (apkSignatureSchemeIds.Any())
            {
                // Add APK Signature Scheme v2 (and newer) signature stripping protection.
                // This attribute indicates that this APK is supposed to have been signed using one or
                // more APK-specific signature schemes in addition to the standard JAR signature scheme
                // used by this code. APK signature verifier should reject the APK if it does not
                // contain a signature for the signature scheme the verifier prefers out of this set.
                var attrValue = new StringBuilder();
                foreach (var id in apkSignatureSchemeIds)
                {
                    if (attrValue.Length > 0)
                    {
                        attrValue.Append(", ");
                    }

                    attrValue.Append(id);
                }

                mainAttrs.Add(
                    SfAttributeNameAndroidApkSignedName,
                    attrValue.ToString());
            }

            // Add main attribute containing the digest of MANIFEST.MF.
            var md = GetMessageDigestInstance(manifestDigestAlgorithm);

            mainAttrs.Add(manifestDigestAlgorithm.DigestManifestAttributeName,
                          Convert.ToBase64String(md.ComputeHash(manifest.Contents)));
            var @out = new MemoryStream();

            SignatureFileWriter.WriteMainSection(@out, mainAttrs);

            var entryDigestAttributeName = GetEntryDigestAttributeName(manifestDigestAlgorithm);

            foreach (var manifestSection in manifest.IndividualSectionsContents)
            {
                var sectionName     = manifestSection.Key;
                var sectionContents = manifestSection.Value;
                var sectionDigest   = md.ComputeHash(sectionContents);
                var attrs           = new Dictionary <string, string>();
                attrs.Add(
                    entryDigestAttributeName,
                    Convert.ToBase64String(sectionDigest));
                SignatureFileWriter.WriteIndividualSection(@out, sectionName, attrs);
            }

            // A bug in the java.util.jar implementation of Android platforms up to version 1.6 will
            // cause a spurious IOException to be thrown if the length of the signature file is a
            // multiple of 1024 bytes. As a workaround, add an extra CRLF in this case.
            if ((@out.Length > 0) && ((@out.Length % 1024) == 0))
            {
                SignatureFileWriter.WriteSectionDelimiter(@out);
            }

            return(@out.ToArray());
        }
        /// <summary>
        /// Generated and returns the <code>META-INF/MANIFEST.MF</code> file based on the provided (optional)
        /// input <code>MANIFEST.MF</code> and digests of JAR entries covered by the manifest.
        /// </summary>
        /// <param name="jarEntryDigestAlgorithm"></param>
        /// <param name="jarEntryDigests"></param>
        /// <param name="sourceManifestBytes"></param>
        /// <returns></returns>
        public static OutputManifestFile GenerateManifestFile(DigestAlgorithm jarEntryDigestAlgorithm,
                                                              IDictionary <string, byte[]> jarEntryDigests, byte[] sourceManifestBytes)
        {
            Manifest.Manifest sourceManifest = null;
            if (sourceManifestBytes != null)
            {
                try
                {
                    sourceManifest = new Manifest.Manifest(sourceManifestBytes);
                }
                catch (IOException e)
                {
                    throw new ApkFormatException("Malformed source META-INF/MANIFEST.MF", e);
                }
            }

            var manifestOut = new MemoryStream();
            var mainAttrs   = new Dictionary <string, string>();

            // Copy the main section from the source manifest (if provided). Otherwise use defaults.
            // NOTE: We don't output our own Created-By header because this signer did not create the
            //       JAR/APK being signed -- the signer only adds signatures to the already existing
            //       JAR/APK.
            if (sourceManifest != null)
            {
                foreach (var kvp in sourceManifest.MainAttributes)
                {
                    mainAttrs.Add(kvp.Key, kvp.Value);
                }
            }
            else
            {
                mainAttrs.Add(Manifest.Manifest.ManifestVersion, AttributeValueManifestVersion);
            }

            try
            {
                ManifestWriter.WriteMainSection(manifestOut, mainAttrs);
            }
            catch (IOException e)
            {
                throw new Exception("Failed to write in-memory MANIFEST.MF", e);
            }

            var sortedEntryNames = new List <string>(jarEntryDigests.Keys);

            sortedEntryNames.Sort(StringComparer.Ordinal);


            var invidualSectionsContents = new SortedDictionary <string, byte[]>(StringComparer.Ordinal);
            var entryDigestAttributeName = GetEntryDigestAttributeName(jarEntryDigestAlgorithm);

            foreach (var entryName in sortedEntryNames)
            {
                CheckEntryNameValid(entryName);
                var entryDigest = jarEntryDigests[entryName];
                var entryAttrs  = new Dictionary <string, string>();
                entryAttrs.Add(
                    entryDigestAttributeName,
                    Convert.ToBase64String(entryDigest));
                var    sectionOut = new MemoryStream();
                byte[] sectionBytes;
                try
                {
                    ManifestWriter.WriteIndividualSection(sectionOut, entryName, entryAttrs);
                    sectionBytes = sectionOut.ToArray();
                    manifestOut.Write(sectionBytes, 0, sectionBytes.Length);
                }
                catch (IOException e)
                {
                    throw new Exception("Failed to write in-memory MANIFEST.MF", e);
                }

                invidualSectionsContents.Add(entryName, sectionBytes);
            }

            var result = new OutputManifestFile
            {
                Contents = manifestOut.ToArray(),
                MainSectionAttributes      = mainAttrs,
                IndividualSectionsContents = invidualSectionsContents
            };

            return(result);
        }