2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.verity;
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;
43 * AndroidVerifiedBootKeystore DEFINITIONS ::=
45 * FormatVersion ::= INTEGER
46 * KeyBag ::= SEQUENCE {
48 * AlgorithmIdentifier ::= SEQUENCE {
49 * algorithm OBJECT IDENTIFIER,
50 * parameters ANY DEFINED BY algorithm OPTIONAL
52 * KeyMaterial ::= RSAPublicKey
55 * Signature ::= AndroidVerifiedBootSignature
59 class BootKey extends ASN1Object
61 private AlgorithmIdentifier algorithmIdentifier;
62 private RSAPublicKey keyMaterial;
64 public BootKey(PublicKey key) throws Exception {
65 java.security.interfaces.RSAPublicKey k =
66 (java.security.interfaces.RSAPublicKey) key;
67 this.keyMaterial = new RSAPublicKey(
69 k.getPublicExponent());
70 this.algorithmIdentifier = Utils.getSignatureAlgorithmIdentifier(key);
73 public ASN1Primitive toASN1Primitive() {
74 ASN1EncodableVector v = new ASN1EncodableVector();
75 v.add(algorithmIdentifier);
77 return new DERSequence(v);
80 public void dump() throws Exception {
81 System.out.println(ASN1Dump.dumpAsString(toASN1Primitive()));
85 class BootKeystore extends ASN1Object
87 private ASN1Integer formatVersion;
88 private ASN1EncodableVector keyBag;
89 private BootSignature signature;
90 private X509Certificate certificate;
92 private static final int FORMAT_VERSION = 0;
94 public BootKeystore() {
95 this.formatVersion = new ASN1Integer(FORMAT_VERSION);
96 this.keyBag = new ASN1EncodableVector();
99 public void addPublicKey(byte[] der) throws Exception {
100 PublicKey pubkey = Utils.loadDERPublicKey(der);
101 BootKey k = new BootKey(pubkey);
105 public void setCertificate(X509Certificate cert) {
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();
116 public ASN1Primitive toASN1Primitive() {
117 ASN1EncodableVector v = new ASN1EncodableVector();
118 v.add(formatVersion);
119 v.add(new DERSequence(keyBag));
121 return new DERSequence(v);
124 public void parse(byte[] input) throws Exception {
125 ASN1InputStream stream = new ASN1InputStream(input);
126 ASN1Sequence sequence = (ASN1Sequence) stream.readObject();
128 formatVersion = (ASN1Integer) sequence.getObjectAt(0);
129 if (formatVersion.getValue().intValue() != FORMAT_VERSION) {
130 throw new IllegalArgumentException("Unsupported format version");
133 ASN1Sequence keys = (ASN1Sequence) sequence.getObjectAt(1);
134 Enumeration e = keys.getObjects();
135 while (e.hasMoreElements()) {
136 keyBag.add((ASN1Encodable) e.nextElement());
139 ASN1Object sig = sequence.getObjectAt(2).toASN1Primitive();
140 signature = new BootSignature(sig.getEncoded());
143 public boolean verify() throws Exception {
144 byte[] innerKeystore = getInnerKeystore();
145 return Utils.verify(signature.getPublicKey(), innerKeystore,
146 signature.getSignature(), signature.getAlgorithmIdentifier());
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));
158 public void dump() throws Exception {
159 System.out.println(ASN1Dump.dumpAsString(toASN1Primitive()));
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>");
169 public static void main(String[] args) throws Exception {
170 if (args.length < 2) {
175 Security.addProvider(new BouncyCastleProvider());
176 BootKeystore ks = new BootKeystore();
178 if ("-verify".equals(args[0])) {
179 ks.parse(Utils.read(args[1]));
183 System.err.println("Signature is VALID");
186 System.err.println("Signature is INVALID");
188 } catch (Exception e) {
189 e.printStackTrace(System.err);
193 String privkeyFname = args[0];
194 String certFname = args[1];
195 String outfileFname = args[2];
197 ks.setCertificate(Utils.loadPEMCertificate(certFname));
199 for (int i = 3; i < args.length; i++) {
200 ks.addPublicKey(Utils.read(args[i]));
203 ks.sign(Utils.loadDERPrivateKeyFromFile(privkeyFname));
204 Utils.write(ks.getEncoded(), outfileFname);