/** * Add a copy of the public key to the archive; this should * exactly match one of the files in_ * /system/etc/security/otacerts.zip on the device. (The same * cert can be extracted from the CERT.RSA file but this is much * easier to get at.) */ private static void addOtacert(ZipOutputStream outputJar, X509Certificate2 certificate, DateTime timestamp, Manifest manifest) { HashAlgorithm md = HashAlgorithm.Create("SHA1"); byte[] b = certificate.Export(X509ContentType.Cert); JarEntry je = new JarEntry(OTACERT_NAME); je.DateTime = timestamp; je.Size = b.Length; outputJar.PutNextEntry(je); outputJar.Write(b, 0, b.Length); Attributes attr = new Attributes(); attr.Add("SHA1-Digest", Convert.ToBase64String(md.ComputeHash(b))); manifest.Entries.Add(OTACERT_NAME, attr); }
/** * Copy all the files in a manifest from input to output. We set * the modification times in the output to a fixed time, so as to * reduce variation in the output file and make incremental OTAs * more efficient. */ private static void copyFiles(Manifest manifest, JarFile in_, ZipOutputStream out_, DateTime timestamp) { byte[] buffer = new byte[4096]; int num; IDictionary <String, Attributes> entries = manifest.Entries; List <String> names = new List <String>(entries.Keys); names.Sort(); foreach (String name in names) { JarEntry inEntry = in_.GetJarEntry(name); JarEntry outEntry = null; if (inEntry.CompressionMethod == CompressionMethod.Stored) { // Preserve the STORED method of the input entry. outEntry = new JarEntry(inEntry); } else { // Create a new entry so that the compressed len is recomputed. outEntry = new JarEntry(name); if (inEntry.Size > -1) { outEntry.Size = inEntry.Size; } } outEntry.DateTime = timestamp; out_.PutNextEntry(outEntry); Stream data = in_.GetInputStream(inEntry); while ((num = data.Read(buffer, 0, buffer.Length)) > 0) { out_.Write(buffer, 0, num); } out_.Flush(); } }
/** * Creates a new <code>JarEntry</code> with fields taken from the * specified <code>JarEntry</code> object. * * @param je the <code>JarEntry</code> to copy */ public JarEntry(JarEntry je) : this((ZipEntry)je) { this.attr = je.attr; }
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); } }