diff options
author | Sami Tolvanen <samitolvanen@google.com> | 2014-11-14 14:51:25 +0000 |
---|---|---|
committer | Sami Tolvanen <samitolvanen@google.com> | 2014-11-17 19:02:26 +0000 |
commit | 241f964e10ce8bc6c401073854fdaf1662013dae (patch) | |
tree | de74b829ccf04a67604a2ed8ae9217032ebc6dd4 | |
parent | 40193d94182934b37f4b2ae00fde2402583e59e1 (diff) | |
download | extras-241f964e10ce8bc6c401073854fdaf1662013dae.tar.gz |
Add signature verification to keystore and verity signers
This change adds -verify flags to keystore and verity signers for
verifying the signatures of the signed content generated by the
same tools. This allows implementers of verified boot to test and
verify the correctness of their implementations.
Bug: 15984840
Change-Id: I327de9c3a9e035cd11dc5022e978c840cd37581c
-rw-r--r-- | verity/BootSignature.java | 12 | ||||
-rw-r--r-- | verity/KeystoreSigner.java | 98 | ||||
-rw-r--r-- | verity/KeystoreSigner.mf | 2 | ||||
-rw-r--r-- | verity/VeritySigner.java | 47 | ||||
-rwxr-xr-x[-rw-r--r--] | verity/keystore_signer | 2 |
5 files changed, 136 insertions, 25 deletions
diff --git a/verity/BootSignature.java b/verity/BootSignature.java index c45224a7..03eb32a7 100644 --- a/verity/BootSignature.java +++ b/verity/BootSignature.java @@ -128,6 +128,18 @@ public class BootSignature extends ASN1Object return getAuthenticatedAttributes().getEncoded(); } + public AlgorithmIdentifier getAlgorithmIdentifier() { + return algorithmIdentifier; + } + + public PublicKey getPublicKey() { + return publicKey; + } + + public byte[] getSignature() { + return signature.getOctets(); + } + public void setSignature(byte[] sig, AlgorithmIdentifier algId) { algorithmIdentifier = algId; signature = new DEROctetString(sig); diff --git a/verity/KeystoreSigner.java b/verity/KeystoreSigner.java index 3d946a69..0927d548 100644 --- a/verity/KeystoreSigner.java +++ b/verity/KeystoreSigner.java @@ -21,11 +21,15 @@ import java.security.PrivateKey; import java.security.PublicKey; import java.security.Security; import java.security.Signature; +import java.security.cert.X509Certificate; +import java.util.Enumeration; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1Object; import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERPrintableString; import org.bouncycastle.asn1.DERSequence; @@ -63,8 +67,7 @@ class BootKey extends ASN1Object this.keyMaterial = new RSAPublicKey( k.getModulus(), k.getPublicExponent()); - this.algorithmIdentifier = new AlgorithmIdentifier( - PKCSObjectIdentifiers.sha256WithRSAEncryption); + this.algorithmIdentifier = Utils.getSignatureAlgorithmIdentifier(key); } public ASN1Primitive toASN1Primitive() { @@ -81,12 +84,15 @@ class BootKey extends ASN1Object class BootKeystore extends ASN1Object { - private ASN1Integer formatVersion; - private ASN1EncodableVector keyBag; - private BootSignature signature; + private ASN1Integer formatVersion; + private ASN1EncodableVector keyBag; + private BootSignature signature; + private X509Certificate certificate; + + private static final int FORMAT_VERSION = 0; public BootKeystore() { - this.formatVersion = new ASN1Integer(0); + this.formatVersion = new ASN1Integer(FORMAT_VERSION); this.keyBag = new ASN1EncodableVector(); } @@ -96,6 +102,10 @@ class BootKeystore extends ASN1Object keyBag.add(k); } + public void setCertificate(X509Certificate cert) { + certificate = cert; + } + public byte[] getInnerKeystore() throws Exception { ASN1EncodableVector v = new ASN1EncodableVector(); v.add(formatVersion); @@ -111,10 +121,36 @@ class BootKeystore extends ASN1Object return new DERSequence(v); } + public void parse(byte[] input) throws Exception { + ASN1InputStream stream = new ASN1InputStream(input); + ASN1Sequence sequence = (ASN1Sequence) stream.readObject(); + + formatVersion = (ASN1Integer) sequence.getObjectAt(0); + if (formatVersion.getValue().intValue() != FORMAT_VERSION) { + throw new IllegalArgumentException("Unsupported format version"); + } + + ASN1Sequence keys = (ASN1Sequence) sequence.getObjectAt(1); + Enumeration e = keys.getObjects(); + while (e.hasMoreElements()) { + keyBag.add((ASN1Encodable) e.nextElement()); + } + + ASN1Object sig = sequence.getObjectAt(2).toASN1Primitive(); + signature = new BootSignature(sig.getEncoded()); + } + + public boolean verify() throws Exception { + byte[] innerKeystore = getInnerKeystore(); + return Utils.verify(signature.getPublicKey(), innerKeystore, + signature.getSignature(), signature.getAlgorithmIdentifier()); + } + public void sign(PrivateKey privateKey) throws Exception { byte[] innerKeystore = getInnerKeystore(); byte[] rawSignature = Utils.sign(privateKey, innerKeystore); signature = new BootSignature("keystore", innerKeystore.length); + signature.setCertificate(certificate); signature.setSignature(rawSignature, Utils.getSignatureAlgorithmIdentifier(privateKey)); } @@ -123,19 +159,49 @@ class BootKeystore extends ASN1Object System.out.println(ASN1Dump.dumpAsString(toASN1Primitive())); } - // USAGE: - // AndroidVerifiedBootKeystoreSigner <privkeyFile> <outfile> <pubkeyFile0> ... <pubkeyFileN-1> - // EG: - // java -cp ../../../out/host/common/obj/JAVA_LIBRARIES/AndroidVerifiedBootKeystoreSigner_intermediates/classes/ com.android.verity.AndroidVerifiedBootKeystoreSigner ../../../build/target/product/security/verity_private_dev_key /tmp/keystore.out /tmp/k + private static void usage() { + System.err.println("usage: KeystoreSigner <privatekey.pk8> " + + "<certificate.x509.pem> <outfile> <publickey0.der> " + + "... <publickeyN-1.der> | -verify <keystore>"); + System.exit(1); + } + public static void main(String[] args) throws Exception { + if (args.length < 2) { + usage(); + return; + } + Security.addProvider(new BouncyCastleProvider()); - String privkeyFname = args[0]; - String outfileFname = args[1]; BootKeystore ks = new BootKeystore(); - for (int i=2; i < args.length; i++) { - ks.addPublicKey(Utils.read(args[i])); + + if ("-verify".equals(args[0])) { + ks.parse(Utils.read(args[1])); + + try { + if (ks.verify()) { + System.err.println("Signature is VALID"); + System.exit(0); + } else { + System.err.println("Signature is INVALID"); + } + } catch (Exception e) { + e.printStackTrace(System.err); + } + System.exit(1); + } else { + String privkeyFname = args[0]; + String certFname = args[1]; + String outfileFname = args[2]; + + ks.setCertificate(Utils.loadPEMCertificate(certFname)); + + for (int i = 3; i < args.length; i++) { + ks.addPublicKey(Utils.read(args[i])); + } + + ks.sign(Utils.loadDERPrivateKeyFromFile(privkeyFname)); + Utils.write(ks.getEncoded(), outfileFname); } - ks.sign(Utils.loadDERPrivateKeyFromFile(privkeyFname)); - Utils.write(ks.getEncoded(), outfileFname); } } diff --git a/verity/KeystoreSigner.mf b/verity/KeystoreSigner.mf index a4fee27f..472b7c43 100644 --- a/verity/KeystoreSigner.mf +++ b/verity/KeystoreSigner.mf @@ -1 +1 @@ -Main-Class: com.android.verity.KeystoreSigner
\ No newline at end of file +Main-Class: com.android.verity.BootKeystore diff --git a/verity/VeritySigner.java b/verity/VeritySigner.java index d11878ab..9d857473 100644 --- a/verity/VeritySigner.java +++ b/verity/VeritySigner.java @@ -16,21 +16,54 @@ package com.android.verity; +import java.security.PublicKey; import java.security.PrivateKey; import java.security.Security; +import java.security.cert.X509Certificate; import org.bouncycastle.jce.provider.BouncyCastleProvider; public class VeritySigner { - // USAGE: - // VeritySigner <contentfile> <key.pem> <sigfile> - // To verify that this has correct output: - // openssl rsautl -raw -inkey <key.pem> -encrypt -in <sigfile> > /tmp/dump + private static void usage() { + System.err.println("usage: VeritySigner <contentfile> <key.pk8> " + + "<sigfile> | <contentfile> <certificate.x509.pem> <sigfile> " + + "-verify"); + System.exit(1); + } + public static void main(String[] args) throws Exception { + if (args.length < 3) { + usage(); + return; + } + Security.addProvider(new BouncyCastleProvider()); + byte[] content = Utils.read(args[0]); - PrivateKey privateKey = Utils.loadDERPrivateKey(Utils.read(args[1])); - byte[] signature = Utils.sign(privateKey, content); - Utils.write(signature, args[2]); + + if (args.length > 3 && "-verify".equals(args[3])) { + X509Certificate cert = Utils.loadPEMCertificate(args[1]); + PublicKey publicKey = cert.getPublicKey(); + + byte[] signature = Utils.read(args[2]); + + try { + if (Utils.verify(publicKey, content, signature, + Utils.getSignatureAlgorithmIdentifier(publicKey))) { + System.err.println("Signature is VALID"); + System.exit(0); + } else { + System.err.println("Signature is INVALID"); + } + } catch (Exception e) { + e.printStackTrace(System.err); + } + + System.exit(1); + } else { + PrivateKey privateKey = Utils.loadDERPrivateKey(Utils.read(args[1])); + byte[] signature = Utils.sign(privateKey, content); + Utils.write(signature, args[2]); + } } } diff --git a/verity/keystore_signer b/verity/keystore_signer index 7619c546..445f0c9c 100644..100755 --- a/verity/keystore_signer +++ b/verity/keystore_signer @@ -5,4 +5,4 @@ KEYSTORESIGNER_HOME=`dirname "$0"` KEYSTORESIGNER_HOME=`dirname "$KEYSTORESIGNER_HOME"` -java -Xmx512M -jar "$KEYSTORESIGNER_HOME"/framework/KeystoreSigner.jar "$@"
\ No newline at end of file +java -Xmx512M -jar "$KEYSTORESIGNER_HOME"/framework/BootKeystoreSigner.jar "$@" |