diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/BootSigner.java b/app/src/main/java/com/topjohnwu/magisk/utils/BootSigner.java index 41867aef2..be8fb0456 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/BootSigner.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/BootSigner.java @@ -1,46 +1,56 @@ package com.topjohnwu.magisk.utils; -import android.content.res.AssetManager; +import android.support.annotation.Keep; import com.topjohnwu.crypto.SignBoot; import java.io.FileInputStream; import java.io.InputStream; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; public class BootSigner { + @Keep public static void main(String[] args) throws Exception { - if ("-verify".equals(args[0])) { + if (args.length > 0 && "-verify".equals(args[0])) { String certPath = ""; - if (args.length >= 4 && "-certificate".equals(args[2])) { - /* args[3] is the path to a public key certificate */ - certPath = args[3]; + if (args.length >= 3 && "-certificate".equals(args[1])) { + /* args[2] is the path to a public key certificate */ + certPath = args[2]; } /* args[1] is the path to a signed boot image */ - boolean signed = SignBoot.verifySignature(args[1], + boolean signed = SignBoot.verifySignature(System.in, certPath.isEmpty() ? null : new FileInputStream(certPath)); System.exit(signed ? 0 : 1); - } else { - /* args[0] is the target name, typically /boot - args[1] is the path to a boot image to sign - args[2] is the path where to output the signed boot image - args[3] is the path to a private key - args[4] is the path to the matching public key certificate - */ - InputStream keyIn, sigIn; - if (args.length >= 5) { - keyIn = new FileInputStream(args[3]); - sigIn = new FileInputStream(args[4]); + } else if (args.length > 0 && "-sign".equals(args[0])) { + InputStream keyIn, certIn; + if (args.length >= 3) { + keyIn = new FileInputStream(args[1]); + certIn = new FileInputStream(args[2]); } else { /* Use internal test keys */ - AssetManager asset = Utils.getAssets(System.getProperty("java.class.path")); - if (asset == null) - System.exit(1); - keyIn = asset.open(ZipUtils.PRIVATE_KEY_NAME); - sigIn = asset.open(ZipUtils.PUBLIC_KEY_NAME); + JarFile apk = new JarFile(System.getProperty("java.class.path")); + JarEntry keyEntry = apk.getJarEntry("assets/" + ZipUtils.PRIVATE_KEY_NAME); + JarEntry sigEntry = apk.getJarEntry("assets/" + ZipUtils.PUBLIC_KEY_NAME); + + keyIn = apk.getInputStream(keyEntry); + certIn = apk.getInputStream(sigEntry); } - SignBoot.doSignature(args[0], args[1], args[2], keyIn, sigIn); + boolean success = SignBoot.doSignature("/boot", System.in, System.out, keyIn, certIn); + System.exit(success ? 0 : 1); + } else { + System.err.println( + "BootSigner [args]\n" + + "Input from stdin, outputs to stdout\n" + + "\n" + + "Actions:\n" + + " -verify [x509.pem]\n" + + " verify image, cert is optional\n" + + " -sign [pk8] [x509.pem]\n" + + " sign image, key and cert are optional\n" + ); } } } diff --git a/crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java b/crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java index 4145b5e1b..50d6909a8 100644 --- a/crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java +++ b/crypto/src/main/java/com/topjohnwu/crypto/SignBoot.java @@ -36,57 +36,55 @@ public class SignBoot { Security.addProvider(new BouncyCastleProvider()); } - public static void doSignature(String target, String imagePath, String outPath, - InputStream keyIn, InputStream certIn) throws Exception { - doSignature(target, new FileInputStream(imagePath), - new FileOutputStream(outPath), keyIn, certIn); - } - - public static void doSignature(String target, InputStream imgIn, OutputStream imgOut, - InputStream keyIn, InputStream certIn) throws Exception { - ByteArrayStream bas = new ByteArrayStream(); - bas.readFrom(imgIn); - byte[] image = bas.toByteArray(); - bas.close(); - imgIn.close(); - int signableSize = getSignableImageSize(image); - if (signableSize < image.length) { - System.err.println("NOTE: truncating input from " + - image.length + " to " + signableSize + " bytes"); - image = Arrays.copyOf(image, signableSize); - } else if (signableSize > image.length) { - throw new IllegalArgumentException("Invalid image: too short, expected " + - 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)); - byte[] encoded_bootsig = bootsig.getEncoded(); - imgOut.write(image); - imgOut.write(encoded_bootsig); - imgOut.flush(); - imgOut.close(); - } - - public static boolean verifySignature(String imagePath, InputStream certPath) throws Exception { - ByteArrayStream bas = new ByteArrayStream(); - bas.readFrom(new FileInputStream(imagePath)); - byte[] image = bas.toByteArray(); - bas.close(); - int signableSize = getSignableImageSize(image); - if (signableSize >= image.length) { - System.err.println("Invalid image: not signed"); + public static boolean doSignature(String target, InputStream imgIn, OutputStream imgOut, + InputStream keyIn, InputStream certIn) { + try { + ByteArrayStream bas = new ByteArrayStream(); + bas.readFrom(imgIn); + byte[] image = bas.toByteArray(); + bas.close(); + int signableSize = getSignableImageSize(image); + if (signableSize < image.length) { + System.err.println("NOTE: truncating input from " + + image.length + " to " + signableSize + " bytes"); + image = Arrays.copyOf(image, signableSize); + } else if (signableSize > image.length) { + throw new IllegalArgumentException("Invalid image: too short, expected " + + 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)); + byte[] encoded_bootsig = bootsig.getEncoded(); + imgOut.write(image); + imgOut.write(encoded_bootsig); + imgOut.flush(); + return true; + } catch (Exception e) { + e.printStackTrace(System.err); return false; } - byte[] signature = Arrays.copyOfRange(image, signableSize, image.length); - BootSignature bootsig = new BootSignature(signature); - if (certPath != null) { - bootsig.setCertificate(CryptoUtils.readPublicKey(certPath)); - } + } + + public static boolean verifySignature(InputStream imgIn, InputStream certPath) { try { + ByteArrayStream bas = new ByteArrayStream(); + bas.readFrom(imgIn); + byte[] image = bas.toByteArray(); + bas.close(); + int signableSize = getSignableImageSize(image); + if (signableSize >= image.length) { + System.err.println("Invalid image: not signed"); + return false; + } + byte[] signature = Arrays.copyOfRange(image, signableSize, image.length); + BootSignature bootsig = new BootSignature(signature); + if (certPath != null) { + bootsig.setCertificate(CryptoUtils.readPublicKey(certPath)); + } if (bootsig.verify(Arrays.copyOf(image, signableSize))) { System.err.println("Signature is VALID"); return true; @@ -94,7 +92,8 @@ public class SignBoot { System.err.println("Signature is INVALID"); } } catch (Exception e) { - e.printStackTrace(); + e.printStackTrace(System.err); + System.err.println("Invalid image: not signed"); } return false; }