Finalize bootsigner commandline

This commit is contained in:
topjohnwu 2017-10-31 02:55:50 +08:00
parent 05f41928cd
commit adf930f126
2 changed files with 81 additions and 72 deletions

View File

@ -1,46 +1,56 @@
package com.topjohnwu.magisk.utils; package com.topjohnwu.magisk.utils;
import android.content.res.AssetManager; import android.support.annotation.Keep;
import com.topjohnwu.crypto.SignBoot; import com.topjohnwu.crypto.SignBoot;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class BootSigner { public class BootSigner {
@Keep
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
if ("-verify".equals(args[0])) { if (args.length > 0 && "-verify".equals(args[0])) {
String certPath = ""; String certPath = "";
if (args.length >= 4 && "-certificate".equals(args[2])) { if (args.length >= 3 && "-certificate".equals(args[1])) {
/* args[3] is the path to a public key certificate */ /* args[2] is the path to a public key certificate */
certPath = args[3]; certPath = args[2];
} }
/* args[1] is the path to a signed boot image */ /* 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)); certPath.isEmpty() ? null : new FileInputStream(certPath));
System.exit(signed ? 0 : 1); System.exit(signed ? 0 : 1);
} else { } else if (args.length > 0 && "-sign".equals(args[0])) {
/* args[0] is the target name, typically /boot InputStream keyIn, certIn;
args[1] is the path to a boot image to sign if (args.length >= 3) {
args[2] is the path where to output the signed boot image keyIn = new FileInputStream(args[1]);
args[3] is the path to a private key certIn = new FileInputStream(args[2]);
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 { } else {
/* Use internal test keys */ /* Use internal test keys */
AssetManager asset = Utils.getAssets(System.getProperty("java.class.path")); JarFile apk = new JarFile(System.getProperty("java.class.path"));
if (asset == null) JarEntry keyEntry = apk.getJarEntry("assets/" + ZipUtils.PRIVATE_KEY_NAME);
System.exit(1); JarEntry sigEntry = apk.getJarEntry("assets/" + ZipUtils.PUBLIC_KEY_NAME);
keyIn = asset.open(ZipUtils.PRIVATE_KEY_NAME);
sigIn = asset.open(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 <actions> [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"
);
} }
} }
} }

View File

@ -36,57 +36,55 @@ public class SignBoot {
Security.addProvider(new BouncyCastleProvider()); Security.addProvider(new BouncyCastleProvider());
} }
public static void doSignature(String target, String imagePath, String outPath, public static boolean doSignature(String target, InputStream imgIn, OutputStream imgOut,
InputStream keyIn, InputStream certIn) throws Exception { InputStream keyIn, InputStream certIn) {
doSignature(target, new FileInputStream(imagePath), try {
new FileOutputStream(outPath), keyIn, certIn); ByteArrayStream bas = new ByteArrayStream();
} bas.readFrom(imgIn);
byte[] image = bas.toByteArray();
public static void doSignature(String target, InputStream imgIn, OutputStream imgOut, bas.close();
InputStream keyIn, InputStream certIn) throws Exception { int signableSize = getSignableImageSize(image);
ByteArrayStream bas = new ByteArrayStream(); if (signableSize < image.length) {
bas.readFrom(imgIn); System.err.println("NOTE: truncating input from " +
byte[] image = bas.toByteArray(); image.length + " to " + signableSize + " bytes");
bas.close(); image = Arrays.copyOf(image, signableSize);
imgIn.close(); } else if (signableSize > image.length) {
int signableSize = getSignableImageSize(image); throw new IllegalArgumentException("Invalid image: too short, expected " +
if (signableSize < image.length) { signableSize + " bytes");
System.err.println("NOTE: truncating input from " + }
image.length + " to " + signableSize + " bytes"); BootSignature bootsig = new BootSignature(target, image.length);
image = Arrays.copyOf(image, signableSize); X509Certificate cert = CryptoUtils.readPublicKey(certIn);
} else if (signableSize > image.length) { bootsig.setCertificate(cert);
throw new IllegalArgumentException("Invalid image: too short, expected " + PrivateKey key = CryptoUtils.readPrivateKey(keyIn);
signableSize + " bytes"); bootsig.setSignature(bootsig.sign(image, key),
} CryptoUtils.getSignatureAlgorithmIdentifier(key));
BootSignature bootsig = new BootSignature(target, image.length); byte[] encoded_bootsig = bootsig.getEncoded();
X509Certificate cert = CryptoUtils.readPublicKey(certIn); imgOut.write(image);
bootsig.setCertificate(cert); imgOut.write(encoded_bootsig);
PrivateKey key = CryptoUtils.readPrivateKey(keyIn); imgOut.flush();
bootsig.setSignature(bootsig.sign(image, key), return true;
CryptoUtils.getSignatureAlgorithmIdentifier(key)); } catch (Exception e) {
byte[] encoded_bootsig = bootsig.getEncoded(); e.printStackTrace(System.err);
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");
return false; return false;
} }
byte[] signature = Arrays.copyOfRange(image, signableSize, image.length); }
BootSignature bootsig = new BootSignature(signature);
if (certPath != null) { public static boolean verifySignature(InputStream imgIn, InputStream certPath) {
bootsig.setCertificate(CryptoUtils.readPublicKey(certPath));
}
try { 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))) { if (bootsig.verify(Arrays.copyOf(image, signableSize))) {
System.err.println("Signature is VALID"); System.err.println("Signature is VALID");
return true; return true;
@ -94,7 +92,8 @@ public class SignBoot {
System.err.println("Signature is INVALID"); System.err.println("Signature is INVALID");
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace(System.err);
System.err.println("Invalid image: not signed");
} }
return false; return false;
} }