1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.verity; 18 19 import java.io.IOException; 20 import java.security.PrivateKey; 21 import java.security.PublicKey; 22 import java.security.Security; 23 import java.security.Signature; 24 import java.security.cert.X509Certificate; 25 import java.util.Enumeration; 26 import org.bouncycastle.asn1.ASN1Encodable; 27 import org.bouncycastle.asn1.ASN1EncodableVector; 28 import org.bouncycastle.asn1.ASN1InputStream; 29 import org.bouncycastle.asn1.ASN1Integer; 30 import org.bouncycastle.asn1.ASN1Object; 31 import org.bouncycastle.asn1.ASN1Primitive; 32 import org.bouncycastle.asn1.ASN1Sequence; 33 import org.bouncycastle.asn1.DEROctetString; 34 import org.bouncycastle.asn1.DERPrintableString; 35 import org.bouncycastle.asn1.DERSequence; 36 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 37 import org.bouncycastle.asn1.pkcs.RSAPublicKey; 38 import org.bouncycastle.asn1.util.ASN1Dump; 39 import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 40 import org.bouncycastle.jce.provider.BouncyCastleProvider; 41 42 /** 43 * AndroidVerifiedBootKeystore DEFINITIONS ::= 44 * BEGIN 45 * FormatVersion ::= INTEGER 46 * KeyBag ::= SEQUENCE { 47 * Key ::= SEQUENCE { 48 * AlgorithmIdentifier ::= SEQUENCE { 49 * algorithm OBJECT IDENTIFIER, 50 * parameters ANY DEFINED BY algorithm OPTIONAL 51 * } 52 * KeyMaterial ::= RSAPublicKey 53 * } 54 * } 55 * Signature ::= AndroidVerifiedBootSignature 56 * END 57 */ 58 59 class BootKey extends ASN1Object 60 { 61 private AlgorithmIdentifier algorithmIdentifier; 62 private RSAPublicKey keyMaterial; 63 64 public BootKey(PublicKey key) throws Exception { 65 java.security.interfaces.RSAPublicKey k = 66 (java.security.interfaces.RSAPublicKey) key; 67 this.keyMaterial = new RSAPublicKey( 68 k.getModulus(), 69 k.getPublicExponent()); 70 this.algorithmIdentifier = Utils.getSignatureAlgorithmIdentifier(key); 71 } 72 73 public ASN1Primitive toASN1Primitive() { 74 ASN1EncodableVector v = new ASN1EncodableVector(); 75 v.add(algorithmIdentifier); 76 v.add(keyMaterial); 77 return new DERSequence(v); 78 } 79 80 public void dump() throws Exception { 81 System.out.println(ASN1Dump.dumpAsString(toASN1Primitive())); 82 } 83 } 84 85 class BootKeystore extends ASN1Object 86 { 87 private ASN1Integer formatVersion; 88 private ASN1EncodableVector keyBag; 89 private BootSignature signature; 90 private X509Certificate certificate; 91 92 private static final int FORMAT_VERSION = 0; 93 94 public BootKeystore() { 95 this.formatVersion = new ASN1Integer(FORMAT_VERSION); 96 this.keyBag = new ASN1EncodableVector(); 97 } 98 99 public void addPublicKey(byte[] der) throws Exception { 100 PublicKey pubkey = Utils.loadDERPublicKey(der); 101 BootKey k = new BootKey(pubkey); 102 keyBag.add(k); 103 } 104 105 public void setCertificate(X509Certificate cert) { 106 certificate = cert; 107 } 108 109 public byte[] getInnerKeystore() throws Exception { 110 ASN1EncodableVector v = new ASN1EncodableVector(); 111 v.add(formatVersion); 112 v.add(new DERSequence(keyBag)); 113 return new DERSequence(v).getEncoded(); 114 } 115 116 public ASN1Primitive toASN1Primitive() { 117 ASN1EncodableVector v = new ASN1EncodableVector(); 118 v.add(formatVersion); 119 v.add(new DERSequence(keyBag)); 120 v.add(signature); 121 return new DERSequence(v); 122 } 123 124 public void parse(byte[] input) throws Exception { 125 ASN1InputStream stream = new ASN1InputStream(input); 126 ASN1Sequence sequence = (ASN1Sequence) stream.readObject(); 127 128 formatVersion = (ASN1Integer) sequence.getObjectAt(0); 129 if (formatVersion.getValue().intValue() != FORMAT_VERSION) { 130 throw new IllegalArgumentException("Unsupported format version"); 131 } 132 133 ASN1Sequence keys = (ASN1Sequence) sequence.getObjectAt(1); 134 Enumeration e = keys.getObjects(); 135 while (e.hasMoreElements()) { 136 keyBag.add((ASN1Encodable) e.nextElement()); 137 } 138 139 ASN1Object sig = sequence.getObjectAt(2).toASN1Primitive(); 140 signature = new BootSignature(sig.getEncoded()); 141 } 142 143 public boolean verify() throws Exception { 144 byte[] innerKeystore = getInnerKeystore(); 145 return Utils.verify(signature.getPublicKey(), innerKeystore, 146 signature.getSignature(), signature.getAlgorithmIdentifier()); 147 } 148 149 public void sign(PrivateKey privateKey) throws Exception { 150 byte[] innerKeystore = getInnerKeystore(); 151 byte[] rawSignature = Utils.sign(privateKey, innerKeystore); 152 signature = new BootSignature("keystore", innerKeystore.length); 153 signature.setCertificate(certificate); 154 signature.setSignature(rawSignature, 155 Utils.getSignatureAlgorithmIdentifier(privateKey)); 156 } 157 158 public void dump() throws Exception { 159 System.out.println(ASN1Dump.dumpAsString(toASN1Primitive())); 160 } 161 162 private static void usage() { 163 System.err.println("usage: KeystoreSigner <privatekey.pk8> " + 164 "<certificate.x509.pem> <outfile> <publickey0.der> " + 165 "... <publickeyN-1.der> | -verify <keystore>"); 166 System.exit(1); 167 } 168 169 public static void main(String[] args) throws Exception { 170 if (args.length < 2) { 171 usage(); 172 return; 173 } 174 175 Security.addProvider(new BouncyCastleProvider()); 176 BootKeystore ks = new BootKeystore(); 177 178 if ("-verify".equals(args[0])) { 179 ks.parse(Utils.read(args[1])); 180 181 try { 182 if (ks.verify()) { 183 System.err.println("Signature is VALID"); 184 System.exit(0); 185 } else { 186 System.err.println("Signature is INVALID"); 187 } 188 } catch (Exception e) { 189 e.printStackTrace(System.err); 190 } 191 System.exit(1); 192 } else { 193 String privkeyFname = args[0]; 194 String certFname = args[1]; 195 String outfileFname = args[2]; 196 197 ks.setCertificate(Utils.loadPEMCertificate(certFname)); 198 199 for (int i = 3; i < args.length; i++) { 200 ks.addPublicKey(Utils.read(args[i])); 201 } 202 203 ks.sign(Utils.loadDERPrivateKeyFromFile(privkeyFname)); 204 Utils.write(ks.getEncoded(), outfileFname); 205 } 206 } 207 } 208