1cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra/* 2cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Copyright (C) 2014 The Android Open Source Project 3cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * 4cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Licensed under the Apache License, Version 2.0 (the "License"); 5cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * you may not use this file except in compliance with the License. 6cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * You may obtain a copy of the License at 7cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * 8cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * http://www.apache.org/licenses/LICENSE-2.0 9cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * 10cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Unless required by applicable law or agreed to in writing, software 11cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * distributed under the License is distributed on an "AS IS" BASIS, 12cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * See the License for the specific language governing permissions and 14cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * limitations under the License. 15cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra */ 16cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 17cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condrapackage com.android.verity; 18cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 19cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.io.IOException; 20cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.PrivateKey; 21cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.PublicKey; 22f0f33adb7ce6557459306ce03576af4d79c0c9efSami Tolvanenimport java.security.Security; 23cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.Signature; 24241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanenimport java.security.cert.X509Certificate; 25241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanenimport java.util.Enumeration; 26cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Encodable; 27cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1EncodableVector; 28241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanenimport org.bouncycastle.asn1.ASN1InputStream; 29cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Integer; 30cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Object; 31cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Primitive; 32241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanenimport org.bouncycastle.asn1.ASN1Sequence; 33cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.DEROctetString; 34cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.DERPrintableString; 35cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.DERSequence; 36cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 37cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.pkcs.RSAPublicKey; 38cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.util.ASN1Dump; 39cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.x509.AlgorithmIdentifier; 40f0f33adb7ce6557459306ce03576af4d79c0c9efSami Tolvanenimport org.bouncycastle.jce.provider.BouncyCastleProvider; 41cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 42cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra/** 43cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * AndroidVerifiedBootKeystore DEFINITIONS ::= 44cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * BEGIN 45cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * FormatVersion ::= INTEGER 46cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * KeyBag ::= SEQUENCE { 47cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Key ::= SEQUENCE { 48cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * AlgorithmIdentifier ::= SEQUENCE { 49cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * algorithm OBJECT IDENTIFIER, 50cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * parameters ANY DEFINED BY algorithm OPTIONAL 51cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * } 52cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * KeyMaterial ::= RSAPublicKey 53cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * } 54cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * } 55cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Signature ::= AndroidVerifiedBootSignature 56cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * END 57cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra */ 58cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 59cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraclass BootKey extends ASN1Object 60cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra{ 61cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra private AlgorithmIdentifier algorithmIdentifier; 62cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra private RSAPublicKey keyMaterial; 63cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 64cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public BootKey(PublicKey key) throws Exception { 65cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra java.security.interfaces.RSAPublicKey k = 66cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra (java.security.interfaces.RSAPublicKey) key; 67cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra this.keyMaterial = new RSAPublicKey( 68cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra k.getModulus(), 69cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra k.getPublicExponent()); 70241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen this.algorithmIdentifier = Utils.getSignatureAlgorithmIdentifier(key); 71cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 72cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 73cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public ASN1Primitive toASN1Primitive() { 74cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra ASN1EncodableVector v = new ASN1EncodableVector(); 75cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra v.add(algorithmIdentifier); 76cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra v.add(keyMaterial); 77cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return new DERSequence(v); 78cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 79cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 80cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public void dump() throws Exception { 81cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra System.out.println(ASN1Dump.dumpAsString(toASN1Primitive())); 82cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 83cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra} 84cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 85cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraclass BootKeystore extends ASN1Object 86cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra{ 87241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen private ASN1Integer formatVersion; 88241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen private ASN1EncodableVector keyBag; 89241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen private BootSignature signature; 90241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen private X509Certificate certificate; 91241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 92241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen private static final int FORMAT_VERSION = 0; 93cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 94cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public BootKeystore() { 95241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen this.formatVersion = new ASN1Integer(FORMAT_VERSION); 96cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra this.keyBag = new ASN1EncodableVector(); 97cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 98cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 99cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public void addPublicKey(byte[] der) throws Exception { 100cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra PublicKey pubkey = Utils.loadDERPublicKey(der); 101cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra BootKey k = new BootKey(pubkey); 102cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra keyBag.add(k); 103cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 104cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 105241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen public void setCertificate(X509Certificate cert) { 106241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen certificate = cert; 107241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } 108241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 109cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public byte[] getInnerKeystore() throws Exception { 110cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra ASN1EncodableVector v = new ASN1EncodableVector(); 111cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra v.add(formatVersion); 112cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra v.add(new DERSequence(keyBag)); 113cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return new DERSequence(v).getEncoded(); 114cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 115cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 116cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public ASN1Primitive toASN1Primitive() { 117cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra ASN1EncodableVector v = new ASN1EncodableVector(); 118cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra v.add(formatVersion); 119cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra v.add(new DERSequence(keyBag)); 120cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra v.add(signature); 121cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return new DERSequence(v); 122cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 123cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 124241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen public void parse(byte[] input) throws Exception { 125241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen ASN1InputStream stream = new ASN1InputStream(input); 126241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen ASN1Sequence sequence = (ASN1Sequence) stream.readObject(); 127241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 128241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen formatVersion = (ASN1Integer) sequence.getObjectAt(0); 129241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen if (formatVersion.getValue().intValue() != FORMAT_VERSION) { 130241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen throw new IllegalArgumentException("Unsupported format version"); 131241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } 132241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 133241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen ASN1Sequence keys = (ASN1Sequence) sequence.getObjectAt(1); 134241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen Enumeration e = keys.getObjects(); 135241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen while (e.hasMoreElements()) { 136241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen keyBag.add((ASN1Encodable) e.nextElement()); 137241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } 138241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 139241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen ASN1Object sig = sequence.getObjectAt(2).toASN1Primitive(); 140241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen signature = new BootSignature(sig.getEncoded()); 141241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } 142241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 143241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen public boolean verify() throws Exception { 144241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen byte[] innerKeystore = getInnerKeystore(); 145241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen return Utils.verify(signature.getPublicKey(), innerKeystore, 146241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen signature.getSignature(), signature.getAlgorithmIdentifier()); 147241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } 148241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 149cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public void sign(PrivateKey privateKey) throws Exception { 150cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra byte[] innerKeystore = getInnerKeystore(); 151cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra byte[] rawSignature = Utils.sign(privateKey, innerKeystore); 152cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra signature = new BootSignature("keystore", innerKeystore.length); 153241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen signature.setCertificate(certificate); 15429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence signature.setSignature(rawSignature, 155f0f33adb7ce6557459306ce03576af4d79c0c9efSami Tolvanen Utils.getSignatureAlgorithmIdentifier(privateKey)); 156cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 157cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 158cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public void dump() throws Exception { 159cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra System.out.println(ASN1Dump.dumpAsString(toASN1Primitive())); 160cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 161cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 162241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen private static void usage() { 163241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen System.err.println("usage: KeystoreSigner <privatekey.pk8> " + 164241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen "<certificate.x509.pem> <outfile> <publickey0.der> " + 165241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen "... <publickeyN-1.der> | -verify <keystore>"); 166241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen System.exit(1); 167241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } 168241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 169cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public static void main(String[] args) throws Exception { 170241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen if (args.length < 2) { 171241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen usage(); 172241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen return; 173241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } 174241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 175f0f33adb7ce6557459306ce03576af4d79c0c9efSami Tolvanen Security.addProvider(new BouncyCastleProvider()); 176cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra BootKeystore ks = new BootKeystore(); 177241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 178241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen if ("-verify".equals(args[0])) { 179241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen ks.parse(Utils.read(args[1])); 180241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 181241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen try { 182241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen if (ks.verify()) { 183241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen System.err.println("Signature is VALID"); 184241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen System.exit(0); 185241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } else { 186241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen System.err.println("Signature is INVALID"); 187241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } 188241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } catch (Exception e) { 189241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen e.printStackTrace(System.err); 190241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } 191241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen System.exit(1); 192241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } else { 193241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen String privkeyFname = args[0]; 194241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen String certFname = args[1]; 195241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen String outfileFname = args[2]; 196241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 197241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen ks.setCertificate(Utils.loadPEMCertificate(certFname)); 198241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 199241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen for (int i = 3; i < args.length; i++) { 200241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen ks.addPublicKey(Utils.read(args[i])); 201241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } 202241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 203241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen ks.sign(Utils.loadDERPrivateKeyFromFile(privkeyFname)); 204241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen Utils.write(ks.getEncoded(), outfileFname); 205cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 206cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 20729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence} 208