Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2d867bd2 authored by Doug Zongker's avatar Doug Zongker Committed by Android Git Automerger
Browse files

am 57a5e52d: Merge "add multiple key support to signapk"

* commit '57a5e52d':
  add multiple key support to signapk
parents dd319cef 57a5e52d
Loading
Loading
Loading
Loading
+67 −33
Original line number Diff line number Diff line
@@ -83,6 +83,8 @@ import javax.crypto.spec.PBEKeySpec;
class SignApk {
    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 CERT_SF_MULTI_NAME = "META-INF/CERT%d.SF";
    private static final String CERT_RSA_MULTI_NAME = "META-INF/CERT%d.RSA";

    private static final String OTACERT_NAME = "META-INF/com/android/otacert";

@@ -90,7 +92,8 @@ class SignApk {

    // Files matching this pattern are not copied to the output.
    private static Pattern stripPattern =
            Pattern.compile("^META-INF/(.*)[.](SF|RSA|DSA)$");
        Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA)|com/android/otacert))|(" +
                        Pattern.quote(JarFile.MANIFEST_NAME) + ")$");

    private static X509Certificate readPublicKey(File file)
            throws IOException, GeneralSecurityException {
@@ -208,11 +211,8 @@ class SignApk {

        for (JarEntry entry: byName.values()) {
            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())) {
            if (!entry.isDirectory() &&
                (stripPattern == null || !stripPattern.matcher(name).matches())) {
                InputStream data = jar.getInputStream(entry);
                while ((num = data.read(buffer)) > 0) {
                    md.update(buffer, 0, num);
@@ -499,14 +499,17 @@ class SignApk {
        }
    }

    public static void main(String[] args) {
        if (args.length != 4 && args.length != 5) {
    private static void usage() {
        System.err.println("Usage: signapk [-w] " +
                           "publickey.x509[.pem] privatekey.pk8 " +
                           "[publickey2.x509[.pem] privatekey2.pk8 ...] " +
                           "input.jar output.jar");
        System.exit(2);
    }

    public static void main(String[] args) {
        if (args.length < 4) usage();

        sBouncyCastleProvider = new BouncyCastleProvider();
        Security.addProvider(sBouncyCastleProvider);

@@ -517,25 +520,46 @@ class SignApk {
            argstart = 1;
        }

        if ((args.length - argstart) % 2 == 1) usage();
        int numKeys = ((args.length - argstart) / 2) - 1;
        if (signWholeFile && numKeys > 1) {
            System.err.println("Only one key may be used with -w.");
            System.exit(2);
        }

        String inputFilename = args[args.length-2];
        String outputFilename = args[args.length-1];

        JarFile inputJar = null;
        JarOutputStream outputJar = null;
        FileOutputStream outputFile = null;

        try {
            File publicKeyFile = new File(args[argstart+0]);
            X509Certificate publicKey = readPublicKey(publicKeyFile);
            File firstPublicKeyFile = new File(args[argstart+0]);

            // Assume the certificate is valid for at least an hour.
            long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
            X509Certificate[] publicKey = new X509Certificate[numKeys];
            for (int i = 0; i < numKeys; ++i) {
                int argNum = argstart + i*2;
                publicKey[i] = readPublicKey(new File(args[argNum]));
            }

            PrivateKey privateKey = readPrivateKey(new File(args[argstart+1]));
            inputJar = new JarFile(new File(args[argstart+2]), false);  // Don't verify.
            // Set the ZIP file timestamp to the starting valid time
            // of the 0th certificate plus one hour (to match what
            // we've historically done).
            long timestamp = publicKey[0].getNotBefore().getTime() + 3600L * 1000;

            PrivateKey[] privateKey = new PrivateKey[numKeys];
            for (int i = 0; i < numKeys; ++i) {
                int argNum = argstart + i*2 + 1;
                privateKey[i] = readPrivateKey(new File(args[argNum]));
            }
            inputJar = new JarFile(new File(inputFilename), false);  // Don't verify.

            OutputStream outputStream = null;
            if (signWholeFile) {
                outputStream = new ByteArrayOutputStream();
            } else {
                outputStream = outputFile = new FileOutputStream(args[argstart+3]);
                outputStream = outputFile = new FileOutputStream(outputFilename);
            }
            outputJar = new JarOutputStream(outputStream);

@@ -558,7 +582,7 @@ class SignApk {

            // otacert
            if (signWholeFile) {
                addOtacert(outputJar, publicKeyFile, timestamp, manifest);
                addOtacert(outputJar, firstPublicKeyFile, timestamp, manifest);
            }

            // MANIFEST.MF
@@ -567,30 +591,40 @@ class SignApk {
            outputJar.putNextEntry(je);
            manifest.write(outputJar);

            // CERT.SF
            je = new JarEntry(CERT_SF_NAME);
            je.setTime(timestamp);
            outputJar.putNextEntry(je);
            // In the case of multiple keys, all the .SF files will be
            // identical, but as far as I can tell the jarsigner docs
            // don't allow there to be just one copy in the zipfile;
            // there hase to be one per .RSA file.

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            writeSignatureFile(manifest, baos);
            byte[] signedData = baos.toByteArray();

            for (int k = 0; k < numKeys; ++k) {
                // CERT.SF / CERT#.SF
                je = new JarEntry(numKeys == 1 ? CERT_SF_NAME :
                                  (String.format(CERT_SF_MULTI_NAME, k)));
                je.setTime(timestamp);
                outputJar.putNextEntry(je);
                outputJar.write(signedData);

            // CERT.RSA
            je = new JarEntry(CERT_RSA_NAME);
                // CERT.RSA / CERT#.RSA
                je = new JarEntry(numKeys == 1 ? CERT_RSA_NAME :
                                  (String.format(CERT_RSA_MULTI_NAME, k)));
                je.setTime(timestamp);
                outputJar.putNextEntry(je);
                writeSignatureBlock(new CMSProcessableByteArray(signedData),
                                publicKey, privateKey, outputJar);
                                    publicKey[k], privateKey[k], outputJar);
            }

            outputJar.close();
            outputJar = null;
            outputStream.flush();

            if (signWholeFile) {
                outputFile = new FileOutputStream(args[argstart+3]);
                outputFile = new FileOutputStream(outputFilename);
                signWholeOutputFile(((ByteArrayOutputStream)outputStream).toByteArray(),
                                    outputFile, publicKey, privateKey);
                                    outputFile, publicKey[0], privateKey[0]);
            }
        } catch (Exception e) {
            e.printStackTrace();