aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Nissler2016-02-24 07:45:06 -0600
committerMattias Nissler2016-02-24 07:53:10 -0600
commit03b72b0f377d352a7197b2cbb3c90ebed3321bcc (patch)
tree17deccfc3425d171a8df152db6539d1c22f4c09a
parent4b3cdce702fefa7eac0626f57d09ff018127c191 (diff)
downloadplatform-bootable-recovery-03b72b0f377d352a7197b2cbb3c90ebed3321bcc.tar.gz
platform-bootable-recovery-03b72b0f377d352a7197b2cbb3c90ebed3321bcc.tar.xz
platform-bootable-recovery-03b72b0f377d352a7197b2cbb3c90ebed3321bcc.zip
Move dumpkey tool to the recovery repo.
The dumpkey tool is used to dump encryption keys in a custom format used by the recovery code. Nobody else uses this format AFAICT, so it's more appropriate to keep the code of the tool alongside the recovery code instead of next to mincrypt. BUG:27326256 Change-Id: I30176845617972be1d6e46e9a9218e161fbf0680
-rw-r--r--tools/dumpkey/Android.mk32
-rw-r--r--tools/dumpkey/DumpPublicKey.java270
-rw-r--r--tools/dumpkey/DumpPublicKey.mf1
3 files changed, 303 insertions, 0 deletions
diff --git a/tools/dumpkey/Android.mk b/tools/dumpkey/Android.mk
new file mode 100644
index 00000000..bbd06566
--- /dev/null
+++ b/tools/dumpkey/Android.mk
@@ -0,0 +1,32 @@
1# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15LOCAL_PATH := $(call my-dir)
16
17# Determine whether to build dumpkey from system/core/libmincrypt or from
18# bootable/recovery/tools. The dumpkey source is temporarily present in both
19# locations during the process of moving the tool to the recovery repository.
20# TODO(mnissler): Remove the guard after the transition is complete.
21ifndef BUILD_DUMPKEY_FROM_RECOVERY
22BUILD_DUMPKEY_FROM_RECOVERY := true
23endif
24
25ifeq ($(BUILD_DUMPKEY_FROM_RECOVERY),true)
26include $(CLEAR_VARS)
27LOCAL_MODULE := dumpkey
28LOCAL_SRC_FILES := DumpPublicKey.java
29LOCAL_JAR_MANIFEST := DumpPublicKey.mf
30LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host
31include $(BUILD_HOST_JAVA_LIBRARY)
32endif
diff --git a/tools/dumpkey/DumpPublicKey.java b/tools/dumpkey/DumpPublicKey.java
new file mode 100644
index 00000000..3eb13984
--- /dev/null
+++ b/tools/dumpkey/DumpPublicKey.java
@@ -0,0 +1,270 @@
1/*
2 * Copyright (C) 2008 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
17package com.android.dumpkey;
18
19import org.bouncycastle.jce.provider.BouncyCastleProvider;
20
21import java.io.FileInputStream;
22import java.math.BigInteger;
23import java.security.cert.CertificateFactory;
24import java.security.cert.X509Certificate;
25import java.security.KeyStore;
26import java.security.Key;
27import java.security.PublicKey;
28import java.security.Security;
29import java.security.interfaces.ECPublicKey;
30import java.security.interfaces.RSAPublicKey;
31import java.security.spec.ECPoint;
32
33/**
34 * Command line tool to extract RSA public keys from X.509 certificates
35 * and output source code with data initializers for the keys.
36 * @hide
37 */
38class DumpPublicKey {
39 /**
40 * @param key to perform sanity checks on
41 * @return version number of key. Supported versions are:
42 * 1: 2048-bit RSA key with e=3 and SHA-1 hash
43 * 2: 2048-bit RSA key with e=65537 and SHA-1 hash
44 * 3: 2048-bit RSA key with e=3 and SHA-256 hash
45 * 4: 2048-bit RSA key with e=65537 and SHA-256 hash
46 * @throws Exception if the key has the wrong size or public exponent
47 */
48 static int checkRSA(RSAPublicKey key, boolean useSHA256) throws Exception {
49 BigInteger pubexp = key.getPublicExponent();
50 BigInteger modulus = key.getModulus();
51 int version;
52
53 if (pubexp.equals(BigInteger.valueOf(3))) {
54 version = useSHA256 ? 3 : 1;
55 } else if (pubexp.equals(BigInteger.valueOf(65537))) {
56 version = useSHA256 ? 4 : 2;
57 } else {
58 throw new Exception("Public exponent should be 3 or 65537 but is " +
59 pubexp.toString(10) + ".");
60 }
61
62 if (modulus.bitLength() != 2048) {
63 throw new Exception("Modulus should be 2048 bits long but is " +
64 modulus.bitLength() + " bits.");
65 }
66
67 return version;
68 }
69
70 /**
71 * @param key to perform sanity checks on
72 * @return version number of key. Supported versions are:
73 * 5: 256-bit EC key with curve NIST P-256
74 * @throws Exception if the key has the wrong size or public exponent
75 */
76 static int checkEC(ECPublicKey key) throws Exception {
77 if (key.getParams().getCurve().getField().getFieldSize() != 256) {
78 throw new Exception("Curve must be NIST P-256");
79 }
80
81 return 5;
82 }
83
84 /**
85 * Perform sanity check on public key.
86 */
87 static int check(PublicKey key, boolean useSHA256) throws Exception {
88 if (key instanceof RSAPublicKey) {
89 return checkRSA((RSAPublicKey) key, useSHA256);
90 } else if (key instanceof ECPublicKey) {
91 if (!useSHA256) {
92 throw new Exception("Must use SHA-256 with EC keys!");
93 }
94 return checkEC((ECPublicKey) key);
95 } else {
96 throw new Exception("Unsupported key class: " + key.getClass().getName());
97 }
98 }
99
100 /**
101 * @param key to output
102 * @return a String representing this public key. If the key is a
103 * version 1 key, the string will be a C initializer; this is
104 * not true for newer key versions.
105 */
106 static String printRSA(RSAPublicKey key, boolean useSHA256) throws Exception {
107 int version = check(key, useSHA256);
108
109 BigInteger N = key.getModulus();
110
111 StringBuilder result = new StringBuilder();
112
113 int nwords = N.bitLength() / 32; // # of 32 bit integers in modulus
114
115 if (version > 1) {
116 result.append("v");
117 result.append(Integer.toString(version));
118 result.append(" ");
119 }
120
121 result.append("{");
122 result.append(nwords);
123
124 BigInteger B = BigInteger.valueOf(0x100000000L); // 2^32
125 BigInteger N0inv = B.subtract(N.modInverse(B)); // -1 / N[0] mod 2^32
126
127 result.append(",0x");
128 result.append(N0inv.toString(16));
129
130 BigInteger R = BigInteger.valueOf(2).pow(N.bitLength());
131 BigInteger RR = R.multiply(R).mod(N); // 2^4096 mod N
132
133 // Write out modulus as little endian array of integers.
134 result.append(",{");
135 for (int i = 0; i < nwords; ++i) {
136 long n = N.mod(B).longValue();
137 result.append(n);
138
139 if (i != nwords - 1) {
140 result.append(",");
141 }
142
143 N = N.divide(B);
144 }
145 result.append("}");
146
147 // Write R^2 as little endian array of integers.
148 result.append(",{");
149 for (int i = 0; i < nwords; ++i) {
150 long rr = RR.mod(B).longValue();
151 result.append(rr);
152
153 if (i != nwords - 1) {
154 result.append(",");
155 }
156
157 RR = RR.divide(B);
158 }
159 result.append("}");
160
161 result.append("}");
162 return result.toString();
163 }
164
165 /**
166 * @param key to output
167 * @return a String representing this public key. If the key is a
168 * version 1 key, the string will be a C initializer; this is
169 * not true for newer key versions.
170 */
171 static String printEC(ECPublicKey key) throws Exception {
172 int version = checkEC(key);
173
174 StringBuilder result = new StringBuilder();
175
176 result.append("v");
177 result.append(Integer.toString(version));
178 result.append(" ");
179
180 BigInteger X = key.getW().getAffineX();
181 BigInteger Y = key.getW().getAffineY();
182 int nbytes = key.getParams().getCurve().getField().getFieldSize() / 8; // # of 32 bit integers in X coordinate
183
184 result.append("{");
185 result.append(nbytes);
186
187 BigInteger B = BigInteger.valueOf(0x100L); // 2^8
188
189 // Write out Y coordinate as array of characters.
190 result.append(",{");
191 for (int i = 0; i < nbytes; ++i) {
192 long n = X.mod(B).longValue();
193 result.append(n);
194
195 if (i != nbytes - 1) {
196 result.append(",");
197 }
198
199 X = X.divide(B);
200 }
201 result.append("}");
202
203 // Write out Y coordinate as array of characters.
204 result.append(",{");
205 for (int i = 0; i < nbytes; ++i) {
206 long n = Y.mod(B).longValue();
207 result.append(n);
208
209 if (i != nbytes - 1) {
210 result.append(",");
211 }
212
213 Y = Y.divide(B);
214 }
215 result.append("}");
216
217 result.append("}");
218 return result.toString();
219 }
220
221 static String print(PublicKey key, boolean useSHA256) throws Exception {
222 if (key instanceof RSAPublicKey) {
223 return printRSA((RSAPublicKey) key, useSHA256);
224 } else if (key instanceof ECPublicKey) {
225 return printEC((ECPublicKey) key);
226 } else {
227 throw new Exception("Unsupported key class: " + key.getClass().getName());
228 }
229 }
230
231 public static void main(String[] args) {
232 if (args.length < 1) {
233 System.err.println("Usage: DumpPublicKey certfile ... > source.c");
234 System.exit(1);
235 }
236 Security.addProvider(new BouncyCastleProvider());
237 try {
238 for (int i = 0; i < args.length; i++) {
239 FileInputStream input = new FileInputStream(args[i]);
240 CertificateFactory cf = CertificateFactory.getInstance("X.509");
241 X509Certificate cert = (X509Certificate) cf.generateCertificate(input);
242
243 boolean useSHA256 = false;
244 String sigAlg = cert.getSigAlgName();
245 if ("SHA1withRSA".equals(sigAlg) || "MD5withRSA".equals(sigAlg)) {
246 // SignApk has historically accepted "MD5withRSA"
247 // certificates, but treated them as "SHA1withRSA"
248 // anyway. Continue to do so for backwards
249 // compatibility.
250 useSHA256 = false;
251 } else if ("SHA256withRSA".equals(sigAlg) || "SHA256withECDSA".equals(sigAlg)) {
252 useSHA256 = true;
253 } else {
254 System.err.println(args[i] + ": unsupported signature algorithm \"" +
255 sigAlg + "\"");
256 System.exit(1);
257 }
258
259 PublicKey key = cert.getPublicKey();
260 check(key, useSHA256);
261 System.out.print(print(key, useSHA256));
262 System.out.println(i < args.length - 1 ? "," : "");
263 }
264 } catch (Exception e) {
265 e.printStackTrace();
266 System.exit(1);
267 }
268 System.exit(0);
269 }
270}
diff --git a/tools/dumpkey/DumpPublicKey.mf b/tools/dumpkey/DumpPublicKey.mf
new file mode 100644
index 00000000..7bb3bc88
--- /dev/null
+++ b/tools/dumpkey/DumpPublicKey.mf
@@ -0,0 +1 @@
Main-Class: com.android.dumpkey.DumpPublicKey