OSDN Git Service

am b87c8131: Fix lint warnings.
[android-x86/system-extras.git] / verity / KeystoreSigner.java
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 }