/** Write a .SF file with a digest of the specified manifest. */ private static void writeSignatureFile(Manifest manifest, Stream out_) { Manifest sf = new Manifest(); Attributes main = sf.MainAttributes; main.Add("Signature-Version", "1.0"); main.Add("Created-By", "1.0 (Android SignApk)"); DigestOutputStream digestStream = new DigestOutputStream("SHA1"); StreamWriter print = new StreamWriter(digestStream, new UTF8Encoding()); // Digest of the entire manifest manifest.Write(digestStream); print.Flush(); main.Add("SHA1-Digest-Manifest", Convert.ToBase64String(digestStream.Hash)); IDictionary <String, Attributes> entries = manifest.Entries; foreach (var entry in entries) { // Digest of the manifest stanza for this entry. print.Write("Name: " + entry.Key + "\r\n"); foreach (var att in entry.Value) { print.Write(att.Key + ": " + att.Value + "\r\n"); } print.Write("\r\n"); print.Flush(); Attributes sfAttr = new Attributes(); sfAttr.Add("SHA1-Digest", Convert.ToBase64String(digestStream.Hash)); sf.Entries.Add(entry.Key, sfAttr); } sf.Write(out_); // 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 % 1024) == 0) { var b = Encoding.UTF8.GetBytes("\r\n"); out_.Write(b, 0, b.Length); } }
public static void SignPackage(Stream input, X509Certificate2 certificate, Stream output, bool signWholeFile) { JarFile inputJar = null; ZipOutputStream outputJar = null; // Assume the certificate is valid for at least an hour. DateTime timestamp; DateTime.TryParse(certificate.GetEffectiveDateString(), out timestamp); timestamp = timestamp.AddHours(1); //DateTime timestamp = DateTime.Parse(certificate.GetEffectiveDateString()).AddHours(1); inputJar = new JarFile(input); // Don't verify. Stream outputStream = null; if (signWholeFile) { outputStream = new MemoryStream(); } else { outputStream = output; } outputJar = new ZipOutputStream(outputStream); outputJar.SetComment(null); outputJar.SetLevel(9); JarEntry je; Manifest manifest = addDigestsToManifest(inputJar); // Everything else copyFiles(manifest, inputJar, outputJar, timestamp); // otacert if (signWholeFile) { addOtacert(outputJar, certificate, timestamp, manifest); } var buffer = new MemoryStream(); // MANIFEST.MF je = new JarEntry(JarFile.MANIFEST_NAME); je.DateTime = timestamp; manifest.Write(buffer); je.Size = buffer.Length; outputJar.PutNextEntry(je); buffer.WriteTo(outputJar); // CERT.SF var signature = new MemoryStream(); je = new JarEntry(CERT_SF_NAME); je.DateTime = timestamp; buffer.SetLength(0); writeSignatureFile(manifest, signature); signature.WriteTo(buffer); je.Size = buffer.Length; outputJar.PutNextEntry(je); buffer.WriteTo(outputJar); // CERT.RSA je = new JarEntry(CERT_RSA_NAME); je.DateTime = timestamp; buffer.SetLength(0); writeSignatureBlock(signature, certificate, buffer); je.Size = buffer.Length; outputJar.PutNextEntry(je); buffer.WriteTo(outputJar); outputJar.Close(); outputJar = null; if (signWholeFile) { signWholeOutputFile(((MemoryStream)outputStream).ToArray(), output, certificate); } }