Commit 241f964e authored by Sami Tolvanen's avatar Sami Tolvanen
Browse files

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
parent 40193d94
......@@ -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);
......
......@@ -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);
}
}
Main-Class: com.android.verity.KeystoreSigner
\ No newline at end of file
Main-Class: com.android.verity.BootKeystore
......@@ -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]);
}
}
}
......@@ -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 "$@"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment