From c61135ee7b8031f51f6b3220501359ac23d1ec01 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 27 Jan 2018 00:19:35 +0800 Subject: [PATCH] Embed testkeys into jar --- app | 2 +- build.py | 16 ++---- crypto/build.gradle | 4 +- .../com/topjohnwu/crypto/CryptoUtils.java | 8 +-- .../java/com/topjohnwu/crypto/SignAPK.java | 23 +++++---- .../java/com/topjohnwu/crypto/SignBoot.java | 27 +++++----- .../java/com/topjohnwu/crypto/ZipSigner.java | 46 ++++++++++++------ .../src/main/resources/keys/testkey.pk8 | Bin .../src/main/resources/keys/testkey.x509.pem | 0 9 files changed, 70 insertions(+), 56 deletions(-) rename ziptools/private.key.pk8 => crypto/src/main/resources/keys/testkey.pk8 (100%) rename ziptools/public.certificate.x509.pem => crypto/src/main/resources/keys/testkey.x509.pem (100%) diff --git a/app b/app index 00d655f34..f37f33067 160000 --- a/app +++ b/app @@ -1 +1 @@ -Subproject commit 00d655f346fe6182d508d76d6b75d8e7357fcb9d +Subproject commit f37f330670b1dba2cc76d52d07587c7c605e096a diff --git a/build.py b/build.py index 2df6e4767..201c8bb74 100755 --- a/build.py +++ b/build.py @@ -127,11 +127,6 @@ def build_binary(args): def build_apk(args): header('* Building Magisk Manager') - for key in ['public.certificate.x509.pem', 'private.key.pk8']: - source = os.path.join('ziptools', key) - target = os.path.join('app', 'src', 'main', 'assets', key) - cp(source, target) - for script in ['magisk_uninstaller.sh', 'util_functions.sh']: source = os.path.join('scripts', script) target = os.path.join('app', 'src', 'main', 'assets', script) @@ -331,7 +326,7 @@ def zip_uninstaller(args): sign_adjust_zip(unsigned, output) def sign_adjust_zip(unsigned, output): - signer_name = 'zipsigner-1.1.jar' + signer_name = 'zipsigner-2.0.jar' jarsigner = os.path.join('crypto', 'build', 'libs', signer_name) if os.name != 'nt' and not os.path.exists(os.path.join('ziptools', 'zipadjust')): @@ -348,14 +343,10 @@ def sign_adjust_zip(unsigned, output): header('* Signing / Adjusting Zip') - publicKey = os.path.join('ziptools', 'public.certificate.x509.pem') - privateKey = os.path.join('ziptools', 'private.key.pk8') - signed = tempfile.mkstemp()[1] # Unsigned->signed - proc = subprocess.run(['java', '-jar', jarsigner, - publicKey, privateKey, unsigned, signed]) + proc = subprocess.run(['java', '-jar', jarsigner, unsigned, signed]) if proc.returncode != 0: error('First sign flashable zip failed!') @@ -367,8 +358,7 @@ def sign_adjust_zip(unsigned, output): error('Adjust flashable zip failed!') # Adjusted -> output - proc = subprocess.run(['java', '-jar', jarsigner, - "-m", publicKey, privateKey, adjusted, output]) + proc = subprocess.run(['java', '-jar', jarsigner, "-m", adjusted, output]) if proc.returncode != 0: error('Second sign flashable zip failed!') diff --git a/crypto/build.gradle b/crypto/build.gradle index edfb254cd..5edf0b6d1 100644 --- a/crypto/build.gradle +++ b/crypto/build.gradle @@ -15,7 +15,7 @@ jar { shadowJar { baseName = 'zipsigner' classifier = null - version = 1.1 + version = 2.0 } buildscript { @@ -23,7 +23,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1' + classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.2' } } diff --git a/crypto/src/main/java/com/topjohnwu/crypto/CryptoUtils.java b/crypto/src/main/java/com/topjohnwu/crypto/CryptoUtils.java index 45bf1655c..7fd88960b 100644 --- a/crypto/src/main/java/com/topjohnwu/crypto/CryptoUtils.java +++ b/crypto/src/main/java/com/topjohnwu/crypto/CryptoUtils.java @@ -102,7 +102,7 @@ class CryptoUtils { return signer.sign(); } - static X509Certificate readPublicKey(InputStream input) + static X509Certificate readCertificate(InputStream input) throws IOException, GeneralSecurityException { try { CertificateFactory cf = CertificateFactory.getInstance("X.509"); @@ -116,9 +116,9 @@ class CryptoUtils { static PrivateKey readPrivateKey(InputStream input) throws IOException, GeneralSecurityException { try { - byte[] buffer = new byte[4096]; - int size = input.read(buffer); - byte[] bytes = Arrays.copyOf(buffer, size); + ByteArrayStream buf = new ByteArrayStream(); + buf.readFrom(input); + byte[] bytes = buf.toByteArray(); /* Check to see if this is in an EncryptedPrivateKeyInfo structure. */ PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes); /* diff --git a/crypto/src/main/java/com/topjohnwu/crypto/SignAPK.java b/crypto/src/main/java/com/topjohnwu/crypto/SignAPK.java index 6b980778b..44f798f89 100644 --- a/crypto/src/main/java/com/topjohnwu/crypto/SignAPK.java +++ b/crypto/src/main/java/com/topjohnwu/crypto/SignAPK.java @@ -18,11 +18,9 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.util.encoders.Base64; -import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; @@ -69,21 +67,27 @@ public class SignAPK { Security.insertProviderAt(sBouncyCastleProvider, 1); } - public static void signZip(InputStream publicIn, InputStream privateIn, + public static void signZip(InputStream cert, InputStream key, JarMap input, OutputStream output, boolean minSign) throws Exception { int alignment = 4; int hashes = 0; - X509Certificate publicKey = CryptoUtils.readPublicKey(publicIn); - hashes |= getDigestAlgorithm(publicKey); + if (cert == null) { + cert = SignAPK.class.getResourceAsStream("/keys/testkey.x509.pem"); + } + X509Certificate certificate = CryptoUtils.readCertificate(cert); + hashes |= getDigestAlgorithm(certificate); // 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.getNotBefore().getTime() + 3600L * 1000; - PrivateKey privateKey = CryptoUtils.readPrivateKey(privateIn); + long timestamp = certificate.getNotBefore().getTime() + 3600L * 1000; + if (key == null) { + key = SignAPK.class.getResourceAsStream("/keys/testkey.pk8"); + } + PrivateKey privateKey = CryptoUtils.readPrivateKey(key); if (minSign) { - signWholeFile(input.getFile(), publicKey, privateKey, output); + signWholeFile(input.getFile(), certificate, privateKey, output); } else { JarOutputStream outputJar = new JarOutputStream(output); // For signing .apks, use the maximum compression to make @@ -95,7 +99,8 @@ public class SignAPK { outputJar.setLevel(9); Manifest manifest = addDigestsToManifest(input, hashes); copyFiles(manifest, input, outputJar, timestamp, alignment); - signFile(manifest, input, publicKey, privateKey, outputJar); + signFile(manifest, input, certificate, privateKey, outputJar); + outputJar.close(); } } diff --git a/crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java b/crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java index beb434274..0bf86d1d2 100644 --- a/crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java +++ b/crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java @@ -35,7 +35,7 @@ public class SignBoot { } public static boolean doSignature(String target, InputStream imgIn, OutputStream imgOut, - InputStream keyIn, InputStream certIn) { + InputStream cert, InputStream key) { try { ByteArrayStream bas = new ByteArrayStream(); bas.readFrom(imgIn); @@ -51,23 +51,29 @@ public class SignBoot { signableSize + " bytes"); } BootSignature bootsig = new BootSignature(target, image.length); - X509Certificate cert = CryptoUtils.readPublicKey(certIn); - bootsig.setCertificate(cert); - PrivateKey key = CryptoUtils.readPrivateKey(keyIn); - bootsig.setSignature(bootsig.sign(image, key), - CryptoUtils.getSignatureAlgorithmIdentifier(key)); + if (cert == null) { + cert = SignBoot.class.getResourceAsStream("/keys/testkey.x509.pem"); + } + X509Certificate certificate = CryptoUtils.readCertificate(cert); + bootsig.setCertificate(certificate); + if (key == null) { + key = SignBoot.class.getResourceAsStream("/keys/testkey.pk8"); + } + PrivateKey privateKey = CryptoUtils.readPrivateKey(key); + bootsig.setSignature(bootsig.sign(image, privateKey), + CryptoUtils.getSignatureAlgorithmIdentifier(privateKey)); byte[] encoded_bootsig = bootsig.getEncoded(); imgOut.write(image); imgOut.write(encoded_bootsig); imgOut.flush(); return true; } catch (Exception e) { - e.printStackTrace(System.err); + e.printStackTrace(); return false; } } - public static boolean verifySignature(InputStream imgIn, InputStream certPath) { + public static boolean verifySignature(InputStream imgIn, InputStream certIn) { try { ByteArrayStream bas = new ByteArrayStream(); bas.readFrom(imgIn); @@ -80,8 +86,8 @@ public class SignBoot { } byte[] signature = Arrays.copyOfRange(image, signableSize, image.length); BootSignature bootsig = new BootSignature(signature); - if (certPath != null) { - bootsig.setCertificate(CryptoUtils.readPublicKey(certPath)); + if (certIn != null) { + bootsig.setCertificate(CryptoUtils.readCertificate(certIn)); } if (bootsig.verify(Arrays.copyOf(image, signableSize))) { System.err.println("Signature is VALID"); @@ -90,7 +96,6 @@ public class SignBoot { System.err.println("Signature is INVALID"); } } catch (Exception e) { - e.printStackTrace(System.err); System.err.println("Invalid image: not signed"); } return false; diff --git a/crypto/src/main/java/com/topjohnwu/crypto/ZipSigner.java b/crypto/src/main/java/com/topjohnwu/crypto/ZipSigner.java index 86be876b7..c41a5c623 100644 --- a/crypto/src/main/java/com/topjohnwu/crypto/ZipSigner.java +++ b/crypto/src/main/java/com/topjohnwu/crypto/ZipSigner.java @@ -2,44 +2,58 @@ package com.topjohnwu.crypto; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.security.Security; public class ZipSigner { - public static void main(String[] args) { + + public static void usage() { + System.err.println("Usage: zipsigner [-m] [x509.pem] [pk8] input.jar output.jar"); + System.err.println("If no certificate/private key pair is specified, it will use the embedded test keys."); + System.err.println(" -m: enable minimal signing"); + System.exit(2); + } + + public static void main(String[] args) throws Exception { boolean minSign = false; int argStart = 0; - if (args.length < 4) { - System.err.println("Usage: zipsigner [-m] publickey.x509[.pem] privatekey.pk8 input.jar output.jar"); - System.exit(2); - } + if (args.length < 2) + usage(); if (args[0].equals("-m")) { minSign = true; argStart = 1; } + InputStream cert = null; + InputStream key = null; + + if (args.length - argStart == 4) { + cert = new BufferedInputStream(new FileInputStream(new File(args[argStart]))); + key = new BufferedInputStream(new FileInputStream(new File(args[argStart + 1]))); + argStart += 2; + } + + if (args.length - argStart != 2) + usage(); + SignAPK.sBouncyCastleProvider = new BouncyCastleProvider(); Security.insertProviderAt(SignAPK.sBouncyCastleProvider, 1); - File pubKey = new File(args[argStart]); - File privKey = new File(args[argStart + 1]); - File input = new File(args[argStart + 2]); - File output = new File(args[argStart + 3]); + File input = new File(args[argStart]); + File output = new File(args[argStart + 1]); - try (InputStream pub = new FileInputStream(pubKey); - InputStream priv = new FileInputStream(privKey); - JarMap jar = new JarMap(input, false); + try (JarMap jar = new JarMap(input, false); BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(output))) { - SignAPK.signZip(pub, priv, jar, out, minSign); - } catch (Exception e) { - e.printStackTrace(); - System.exit(1); + SignAPK.signZip(cert, key, jar, out, minSign); } } } diff --git a/ziptools/private.key.pk8 b/crypto/src/main/resources/keys/testkey.pk8 similarity index 100% rename from ziptools/private.key.pk8 rename to crypto/src/main/resources/keys/testkey.pk8 diff --git a/ziptools/public.certificate.x509.pem b/crypto/src/main/resources/keys/testkey.x509.pem similarity index 100% rename from ziptools/public.certificate.x509.pem rename to crypto/src/main/resources/keys/testkey.x509.pem