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); }