diff options
author | Doug Zongker <dougz@android.com> | 2009-08-12 18:20:24 -0700 |
---|---|---|
committer | Doug Zongker <dougz@android.com> | 2009-08-14 12:25:05 -0700 |
commit | c6cf01a1170c3a7a5b03135b01cf97f06e1b953d (patch) | |
tree | 72160be40b0cf88c3e70cabc1aa62c378b0c956e /tools/signapk | |
parent | 9af298ec82fb93d0259e2080ce022204fe49e640 (diff) | |
download | build-c6cf01a1170c3a7a5b03135b01cf97f06e1b953d.zip build-c6cf01a1170c3a7a5b03135b01cf97f06e1b953d.tar.gz build-c6cf01a1170c3a7a5b03135b01cf97f06e1b953d.tar.bz2 |
add whole-file signature mode to SignApk
Make SignApk generate a signature for (nearly) the entire zip file
when run with the -w option. The signature covers all of the zip file
except for the archive comment (conveniently the last thing in a zip
file); the archive comment field is used to contain the signature
itself.
Diffstat (limited to 'tools/signapk')
-rw-r--r-- | tools/signapk/SignApk.java | 91 |
1 files changed, 83 insertions, 8 deletions
diff --git a/tools/signapk/SignApk.java b/tools/signapk/SignApk.java index caf7935..73bb3fe 100644 --- a/tools/signapk/SignApk.java +++ b/tools/signapk/SignApk.java @@ -247,7 +247,7 @@ class SignApk { } } - /** Write a .SF file with a digest the specified manifest. */ + /** Write a .SF file with a digest of the specified manifest. */ private static void writeSignatureFile(Manifest manifest, OutputStream out) throws IOException, GeneralSecurityException { Manifest sf = new Manifest(); @@ -304,6 +304,56 @@ class SignApk { pkcs7.encodeSignedData(out); } + private static void signWholeOutputFile(byte[] zipData, + OutputStream outputStream, + X509Certificate publicKey, + PrivateKey privateKey) + throws IOException, GeneralSecurityException { + + // For a zip with no archive comment, the + // end-of-central-directory record will be 22 bytes long, so + // we expect to find the EOCD marker 22 bytes from the end. + if (zipData[zipData.length-22] != 0x50 || + zipData[zipData.length-21] != 0x4b || + zipData[zipData.length-20] != 0x05 || + zipData[zipData.length-19] != 0x06) { + throw new IllegalArgumentException("zip data already has an archive comment"); + } + + Signature signature = Signature.getInstance("SHA1withRSA"); + signature.initSign(privateKey); + signature.update(zipData, 0, zipData.length-2); + + ByteArrayOutputStream temp = new ByteArrayOutputStream(); + + // put a readable message and a null char at the start of the + // archive comment, so that tools that display the comment + // (hopefully) show something sensible. + // TODO: anything more useful we can put in this message? + byte[] message = "signed by SignApk".getBytes("UTF-8"); + temp.write(message); + temp.write(0); + writeSignatureBlock(signature, publicKey, temp); + int total_size = temp.size() + 6; + if (total_size > 0xffff) { + throw new IllegalArgumentException("signature is too big for ZIP file comment"); + } + // signature starts this many bytes from the end of the file + int signature_start = total_size - message.length - 1; + temp.write(0xff); + temp.write(0xff); + temp.write(signature_start & 0xff); + temp.write((signature_start >> 8) & 0xff); + temp.write(total_size & 0xff); + temp.write((total_size >> 8) & 0xff); + temp.flush(); + + outputStream.write(zipData, 0, zipData.length-2); + outputStream.write(total_size & 0xff); + outputStream.write((total_size >> 8) & 0xff); + temp.writeTo(outputStream); + } + /** * 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 @@ -340,25 +390,40 @@ class SignApk { } public static void main(String[] args) { - if (args.length != 4) { - System.err.println("Usage: signapk " + + if (args.length != 4 && args.length != 5) { + System.err.println("Usage: signapk [-w] " + "publickey.x509[.pem] privatekey.pk8 " + "input.jar output.jar"); System.exit(2); } + boolean signWholeFile = false; + int argstart = 0; + if (args[0].equals("-w")) { + signWholeFile = true; + argstart = 1; + } + JarFile inputJar = null; JarOutputStream outputJar = null; + FileOutputStream outputFile = null; try { - X509Certificate publicKey = readPublicKey(new File(args[0])); + X509Certificate publicKey = readPublicKey(new File(args[argstart+0])); // Assume the certificate is valid for at least an hour. long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000; - PrivateKey privateKey = readPrivateKey(new File(args[1])); - inputJar = new JarFile(new File(args[2]), false); // Don't verify. - outputJar = new JarOutputStream(new FileOutputStream(args[3])); + PrivateKey privateKey = readPrivateKey(new File(args[argstart+1])); + inputJar = new JarFile(new File(args[argstart+2]), false); // Don't verify. + + OutputStream outputStream = null; + if (signWholeFile) { + outputStream = new ByteArrayOutputStream(); + } else { + outputStream = outputFile = new FileOutputStream(args[argstart+3]); + } + outputJar = new JarOutputStream(outputStream); outputJar.setLevel(9); JarEntry je; @@ -387,13 +452,23 @@ class SignApk { // Everything else copyFiles(manifest, inputJar, outputJar, timestamp); + + outputJar.close(); + outputJar = null; + outputStream.flush(); + + if (signWholeFile) { + outputFile = new FileOutputStream(args[argstart+3]); + signWholeOutputFile(((ByteArrayOutputStream)outputStream).toByteArray(), + outputFile, publicKey, privateKey); + } } catch (Exception e) { e.printStackTrace(); System.exit(1); } finally { try { if (inputJar != null) inputJar.close(); - if (outputJar != null) outputJar.close(); + if (outputFile != null) outputFile.close(); } catch (IOException e) { e.printStackTrace(); System.exit(1); |