add copy of public cert used for signing to OTA packages

When signing a file with -w (ie, an OTA package), add the file
META-INF/com/android/otacert, which is a copy of the public key
certificate.  While this can be extracted from the CERT.RSA file,
having a copy of it more easily accessible makes it easier to write
tools.

Bug: 6477365
Change-Id: I8cdb19536eca9a223c2b954e3f8ea0d9f3f86f02
diff --git a/tools/signapk/SignApk.java b/tools/signapk/SignApk.java
index c4d73c8..d8d9bf1 100644
--- a/tools/signapk/SignApk.java
+++ b/tools/signapk/SignApk.java
@@ -76,6 +76,8 @@
     private static final String CERT_SF_NAME = "META-INF/CERT.SF";
     private static final String CERT_RSA_NAME = "META-INF/CERT.RSA";
 
+    private static final String OTACERT_NAME = "META-INF/com/android/otacert";
+
     // Files matching this pattern are not copied to the output.
     private static Pattern stripPattern =
             Pattern.compile("^META-INF/(.*)[.](SF|RSA|DSA)$");
@@ -199,6 +201,7 @@
             String name = entry.getName();
             if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) &&
                 !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) &&
+                !name.equals(OTACERT_NAME) &&
                 (stripPattern == null ||
                  !stripPattern.matcher(name).matches())) {
                 InputStream data = jar.getInputStream(entry);
@@ -217,6 +220,39 @@
         return output;
     }
 
+    /**
+     * 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(JarOutputStream outputJar,
+                                   File publicKeyFile,
+                                   long timestamp,
+                                   Manifest manifest)
+        throws IOException, GeneralSecurityException {
+        BASE64Encoder base64 = new BASE64Encoder();
+        MessageDigest md = MessageDigest.getInstance("SHA1");
+
+        JarEntry je = new JarEntry(OTACERT_NAME);
+        je.setTime(timestamp);
+        outputJar.putNextEntry(je);
+        FileInputStream input = new FileInputStream(publicKeyFile);
+        byte[] b = new byte[4096];
+        int read;
+        while ((read = input.read(b)) != -1) {
+            outputJar.write(b, 0, read);
+            md.update(b, 0, read);
+        }
+        input.close();
+
+        Attributes attr = new Attributes();
+        attr.putValue("SHA1-Digest", base64.encode(md.digest()));
+        manifest.getEntries().put(OTACERT_NAME, attr);
+    }
+
+
     /** Write to another stream and also feed it to the Signature object. */
     private static class SignatureOutputStream extends FilterOutputStream {
         private Signature mSignature;
@@ -445,7 +481,8 @@
         FileOutputStream outputFile = null;
 
         try {
-            X509Certificate publicKey = readPublicKey(new File(args[argstart+0]));
+            File publicKeyFile = new File(args[argstart+0]);
+            X509Certificate publicKey = readPublicKey(publicKeyFile);
 
             // Assume the certificate is valid for at least an hour.
             long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
@@ -464,8 +501,17 @@
 
             JarEntry je;
 
-            // MANIFEST.MF
             Manifest manifest = addDigestsToManifest(inputJar);
+
+            // Everything else
+            copyFiles(manifest, inputJar, outputJar, timestamp);
+
+            // otacert
+            if (signWholeFile) {
+                addOtacert(outputJar, publicKeyFile, timestamp, manifest);
+            }
+
+            // MANIFEST.MF
             je = new JarEntry(JarFile.MANIFEST_NAME);
             je.setTime(timestamp);
             outputJar.putNextEntry(je);
@@ -486,9 +532,6 @@
             outputJar.putNextEntry(je);
             writeSignatureBlock(signature, publicKey, outputJar);
 
-            // Everything else
-            copyFiles(manifest, inputJar, outputJar, timestamp);
-
             outputJar.close();
             outputJar = null;
             outputStream.flush();