blob: c259c25b0e5cd514937843ac6daad4412eaa99c7 [file] [log] [blame]
Kenny Roote29df162012-08-10 08:28:37 -07001/*
2 * Copyright (C) 2012 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 android.security;
18
Kenny Root12e75222013-04-23 22:34:24 -070019import com.android.org.conscrypt.OpenSSLEngine;
20import com.android.org.conscrypt.OpenSSLKeyHolder;
Kenny Roote29df162012-08-10 08:28:37 -070021
Alex Klyubin5927c9f2015-04-10 13:28:03 -070022import libcore.util.EmptyArray;
23
Alex Klyubinbaf28382015-03-26 14:46:55 -070024import android.security.keymaster.KeyCharacteristics;
25import android.security.keymaster.KeymasterArguments;
26import android.security.keymaster.KeymasterDefs;
Kenny Roote29df162012-08-10 08:28:37 -070027import android.util.Log;
28
29import java.io.ByteArrayInputStream;
30import java.io.IOException;
31import java.io.InputStream;
32import java.io.OutputStream;
33import java.security.InvalidKeyException;
34import java.security.Key;
Kenny Root2eeda722013-04-10 11:30:58 -070035import java.security.KeyStore.Entry;
36import java.security.KeyStore.PrivateKeyEntry;
37import java.security.KeyStore.ProtectionParameter;
38import java.security.KeyStore;
Alex Klyubinbaf28382015-03-26 14:46:55 -070039import java.security.KeyStore.SecretKeyEntry;
Kenny Roote29df162012-08-10 08:28:37 -070040import java.security.KeyStoreException;
41import java.security.KeyStoreSpi;
42import java.security.NoSuchAlgorithmException;
43import java.security.PrivateKey;
44import java.security.UnrecoverableKeyException;
45import java.security.cert.Certificate;
46import java.security.cert.CertificateEncodingException;
47import java.security.cert.CertificateException;
48import java.security.cert.CertificateFactory;
49import java.security.cert.X509Certificate;
50import java.util.ArrayList;
Alex Klyubin5927c9f2015-04-10 13:28:03 -070051import java.util.Arrays;
Kenny Roote29df162012-08-10 08:28:37 -070052import java.util.Collection;
53import java.util.Collections;
54import java.util.Date;
55import java.util.Enumeration;
56import java.util.HashSet;
57import java.util.Iterator;
Alex Klyubin5eacd772015-04-14 19:00:35 -070058import java.util.List;
Kenny Roote29df162012-08-10 08:28:37 -070059import java.util.Set;
60
Alex Klyubinbaf28382015-03-26 14:46:55 -070061import javax.crypto.SecretKey;
62
Kenny Roote29df162012-08-10 08:28:37 -070063/**
Kenny Rootdb026712012-08-20 10:48:46 -070064 * A java.security.KeyStore interface for the Android KeyStore. An instance of
65 * it can be created via the {@link java.security.KeyStore#getInstance(String)
Kenny Roote29df162012-08-10 08:28:37 -070066 * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a
67 * java.security.KeyStore backed by this "AndroidKeyStore" implementation.
68 * <p>
69 * This is built on top of Android's keystore daemon. The convention of alias
70 * use is:
71 * <p>
72 * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key,
73 * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one
74 * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE
75 * entry which will have the rest of the chain concatenated in BER format.
76 * <p>
77 * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry
78 * with a single certificate.
79 *
80 * @hide
81 */
82public class AndroidKeyStore extends KeyStoreSpi {
83 public static final String NAME = "AndroidKeyStore";
84
85 private android.security.KeyStore mKeyStore;
86
87 @Override
88 public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
89 UnrecoverableKeyException {
Alex Klyubinbaf28382015-03-26 14:46:55 -070090 if (isPrivateKeyEntry(alias)) {
91 final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
92 try {
93 return engine.getPrivateKeyById(Credentials.USER_PRIVATE_KEY + alias);
94 } catch (InvalidKeyException e) {
95 UnrecoverableKeyException t = new UnrecoverableKeyException("Can't get key");
96 t.initCause(e);
97 throw t;
98 }
99 } else if (isSecretKeyEntry(alias)) {
100 KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
101 String keyAliasInKeystore = Credentials.USER_SECRET_KEY + alias;
102 int errorCode = mKeyStore.getKeyCharacteristics(
103 keyAliasInKeystore, null, null, keyCharacteristics);
104 if ((errorCode != KeymasterDefs.KM_ERROR_OK)
105 && (errorCode != android.security.KeyStore.NO_ERROR)) {
106 throw new UnrecoverableKeyException("Failed to load information about key."
107 + " Error code: " + errorCode);
108 }
109
110 int keymasterAlgorithm =
111 keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
112 if (keymasterAlgorithm == -1) {
113 keymasterAlgorithm =
114 keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
115 }
116 if (keymasterAlgorithm == -1) {
117 throw new UnrecoverableKeyException("Key algorithm unknown");
118 }
Alex Klyubinbaf28382015-03-26 14:46:55 -0700119
Alex Klyubin5eacd772015-04-14 19:00:35 -0700120 List<Integer> keymasterDigests =
121 keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST);
122 int keymasterDigest;
123 if (keymasterDigests.isEmpty()) {
124 keymasterDigest = -1;
125 } else {
126 // More than one digest can be permitted for this key. Use the first one to form the
127 // JCA key algorithm name.
128 keymasterDigest = keymasterDigests.get(0);
Alex Klyubinbaf28382015-03-26 14:46:55 -0700129 }
Alex Klyubinbaf28382015-03-26 14:46:55 -0700130
131 String keyAlgorithmString;
132 try {
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700133 keyAlgorithmString = KeymasterUtils.getJcaSecretKeyAlgorithm(
134 keymasterAlgorithm, keymasterDigest);
Alex Klyubinbaf28382015-03-26 14:46:55 -0700135 } catch (IllegalArgumentException e) {
136 throw (UnrecoverableKeyException)
137 new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
138 }
139
140 return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmString);
Kenny Roote29df162012-08-10 08:28:37 -0700141 }
142
Alex Klyubinbaf28382015-03-26 14:46:55 -0700143 return null;
Kenny Roote29df162012-08-10 08:28:37 -0700144 }
145
146 @Override
147 public Certificate[] engineGetCertificateChain(String alias) {
Kenny Roota4640c02012-08-31 13:38:11 -0700148 if (alias == null) {
149 throw new NullPointerException("alias == null");
150 }
151
Kenny Roote29df162012-08-10 08:28:37 -0700152 final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias);
153 if (leaf == null) {
154 return null;
155 }
156
157 final Certificate[] caList;
158
159 final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
160 if (caBytes != null) {
161 final Collection<X509Certificate> caChain = toCertificates(caBytes);
162
163 caList = new Certificate[caChain.size() + 1];
164
165 final Iterator<X509Certificate> it = caChain.iterator();
166 int i = 1;
167 while (it.hasNext()) {
168 caList[i++] = it.next();
169 }
170 } else {
171 caList = new Certificate[1];
172 }
173
174 caList[0] = leaf;
175
176 return caList;
177 }
178
179 @Override
180 public Certificate engineGetCertificate(String alias) {
Kenny Roota4640c02012-08-31 13:38:11 -0700181 if (alias == null) {
182 throw new NullPointerException("alias == null");
183 }
184
Kenny Roote29df162012-08-10 08:28:37 -0700185 byte[] certificate = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
186 if (certificate != null) {
187 return toCertificate(certificate);
188 }
189
190 certificate = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
191 if (certificate != null) {
192 return toCertificate(certificate);
193 }
194
195 return null;
196 }
197
198 private static X509Certificate toCertificate(byte[] bytes) {
199 try {
200 final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
201 return (X509Certificate) certFactory
202 .generateCertificate(new ByteArrayInputStream(bytes));
203 } catch (CertificateException e) {
204 Log.w(NAME, "Couldn't parse certificate in keystore", e);
205 return null;
206 }
207 }
208
209 @SuppressWarnings("unchecked")
210 private static Collection<X509Certificate> toCertificates(byte[] bytes) {
211 try {
212 final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
213 return (Collection<X509Certificate>) certFactory
214 .generateCertificates(new ByteArrayInputStream(bytes));
215 } catch (CertificateException e) {
216 Log.w(NAME, "Couldn't parse certificates in keystore", e);
217 return new ArrayList<X509Certificate>();
218 }
219 }
220
221 private Date getModificationDate(String alias) {
222 final long epochMillis = mKeyStore.getmtime(alias);
223 if (epochMillis == -1L) {
224 return null;
225 }
226
227 return new Date(epochMillis);
228 }
229
230 @Override
231 public Date engineGetCreationDate(String alias) {
Kenny Roota4640c02012-08-31 13:38:11 -0700232 if (alias == null) {
233 throw new NullPointerException("alias == null");
234 }
235
Kenny Roote29df162012-08-10 08:28:37 -0700236 Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias);
237 if (d != null) {
238 return d;
239 }
240
Alex Klyubinbaf28382015-03-26 14:46:55 -0700241 d = getModificationDate(Credentials.USER_SECRET_KEY + alias);
242 if (d != null) {
243 return d;
244 }
245
Kenny Roote29df162012-08-10 08:28:37 -0700246 d = getModificationDate(Credentials.USER_CERTIFICATE + alias);
247 if (d != null) {
248 return d;
249 }
250
251 return getModificationDate(Credentials.CA_CERTIFICATE + alias);
252 }
253
254 @Override
255 public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
256 throws KeyStoreException {
257 if ((password != null) && (password.length > 0)) {
258 throw new KeyStoreException("entries cannot be protected with passwords");
259 }
260
261 if (key instanceof PrivateKey) {
Kenny Root2eeda722013-04-10 11:30:58 -0700262 setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
Alex Klyubinbaf28382015-03-26 14:46:55 -0700263 } else if (key instanceof SecretKey) {
264 setSecretKeyEntry(alias, (SecretKey) key, null);
Kenny Roote29df162012-08-10 08:28:37 -0700265 } else {
Alex Klyubinbaf28382015-03-26 14:46:55 -0700266 throw new KeyStoreException("Only PrivateKey and SecretKey are supported");
Kenny Roote29df162012-08-10 08:28:37 -0700267 }
268 }
269
Kenny Root2eeda722013-04-10 11:30:58 -0700270 private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
Kenny Root1c219f62013-04-18 17:57:03 -0700271 KeyStoreParameter params) throws KeyStoreException {
Kenny Root802768d2012-08-21 15:23:35 -0700272 byte[] keyBytes = null;
273
274 final String pkeyAlias;
Kenny Rootcc1fc6b2013-01-22 13:25:38 -0800275 if (key instanceof OpenSSLKeyHolder) {
276 pkeyAlias = ((OpenSSLKeyHolder) key).getOpenSSLKey().getAlias();
Kenny Root802768d2012-08-21 15:23:35 -0700277 } else {
278 pkeyAlias = null;
Kenny Roote29df162012-08-10 08:28:37 -0700279 }
280
Kenny Root802768d2012-08-21 15:23:35 -0700281 final boolean shouldReplacePrivateKey;
282 if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
283 final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
284 if (!alias.equals(keySubalias)) {
285 throw new KeyStoreException("Can only replace keys with same alias: " + alias
286 + " != " + keySubalias);
287 }
288
289 shouldReplacePrivateKey = false;
290 } else {
291 // Make sure the PrivateKey format is the one we support.
292 final String keyFormat = key.getFormat();
293 if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
294 throw new KeyStoreException(
295 "Only PrivateKeys that can be encoded into PKCS#8 are supported");
296 }
297
298 // Make sure we can actually encode the key.
299 keyBytes = key.getEncoded();
300 if (keyBytes == null) {
301 throw new KeyStoreException("PrivateKey has no encoding");
302 }
303
304 shouldReplacePrivateKey = true;
Kenny Roote29df162012-08-10 08:28:37 -0700305 }
306
307 // Make sure the chain exists since this is a PrivateKey
308 if ((chain == null) || (chain.length == 0)) {
309 throw new KeyStoreException("Must supply at least one Certificate with PrivateKey");
310 }
311
312 // Do chain type checking.
313 X509Certificate[] x509chain = new X509Certificate[chain.length];
314 for (int i = 0; i < chain.length; i++) {
315 if (!"X.509".equals(chain[i].getType())) {
316 throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
317 + i);
318 }
319
320 if (!(chain[i] instanceof X509Certificate)) {
321 throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
322 + i);
323 }
324
325 x509chain[i] = (X509Certificate) chain[i];
326 }
327
328 final byte[] userCertBytes;
329 try {
330 userCertBytes = x509chain[0].getEncoded();
331 } catch (CertificateEncodingException e) {
332 throw new KeyStoreException("Couldn't encode certificate #1", e);
333 }
334
335 /*
336 * If we have a chain, store it in the CA certificate slot for this
337 * alias as concatenated DER-encoded certificates. These can be
338 * deserialized by {@link CertificateFactory#generateCertificates}.
339 */
340 final byte[] chainBytes;
341 if (chain.length > 1) {
342 /*
343 * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...}
344 * so we only need the certificates starting at index 1.
345 */
346 final byte[][] certsBytes = new byte[x509chain.length - 1][];
347 int totalCertLength = 0;
348 for (int i = 0; i < certsBytes.length; i++) {
349 try {
350 certsBytes[i] = x509chain[i + 1].getEncoded();
351 totalCertLength += certsBytes[i].length;
352 } catch (CertificateEncodingException e) {
353 throw new KeyStoreException("Can't encode Certificate #" + i, e);
354 }
355 }
356
357 /*
358 * Serialize this into one byte array so we can later call
359 * CertificateFactory#generateCertificates to recover them.
360 */
361 chainBytes = new byte[totalCertLength];
362 int outputOffset = 0;
363 for (int i = 0; i < certsBytes.length; i++) {
364 final int certLength = certsBytes[i].length;
365 System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength);
366 outputOffset += certLength;
367 certsBytes[i] = null;
368 }
369 } else {
370 chainBytes = null;
371 }
372
373 /*
Kenny Root802768d2012-08-21 15:23:35 -0700374 * Make sure we clear out all the appropriate types before trying to
Kenny Roote29df162012-08-10 08:28:37 -0700375 * write.
376 */
Kenny Root802768d2012-08-21 15:23:35 -0700377 if (shouldReplacePrivateKey) {
378 Credentials.deleteAllTypesForAlias(mKeyStore, alias);
379 } else {
380 Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
Alex Klyubinbaf28382015-03-26 14:46:55 -0700381 Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
Kenny Root802768d2012-08-21 15:23:35 -0700382 }
Kenny Roote29df162012-08-10 08:28:37 -0700383
Kenny Root2eeda722013-04-10 11:30:58 -0700384 final int flags = (params == null) ? 0 : params.getFlags();
385
Kenny Root802768d2012-08-21 15:23:35 -0700386 if (shouldReplacePrivateKey
Kenny Root2eeda722013-04-10 11:30:58 -0700387 && !mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes,
388 android.security.KeyStore.UID_SELF, flags)) {
Kenny Root802768d2012-08-21 15:23:35 -0700389 Credentials.deleteAllTypesForAlias(mKeyStore, alias);
Kenny Roote29df162012-08-10 08:28:37 -0700390 throw new KeyStoreException("Couldn't put private key in keystore");
Kenny Root2eeda722013-04-10 11:30:58 -0700391 } else if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertBytes,
392 android.security.KeyStore.UID_SELF, flags)) {
Kenny Root802768d2012-08-21 15:23:35 -0700393 Credentials.deleteAllTypesForAlias(mKeyStore, alias);
Kenny Roote29df162012-08-10 08:28:37 -0700394 throw new KeyStoreException("Couldn't put certificate #1 in keystore");
395 } else if (chainBytes != null
Kenny Root2eeda722013-04-10 11:30:58 -0700396 && !mKeyStore.put(Credentials.CA_CERTIFICATE + alias, chainBytes,
397 android.security.KeyStore.UID_SELF, flags)) {
Kenny Root802768d2012-08-21 15:23:35 -0700398 Credentials.deleteAllTypesForAlias(mKeyStore, alias);
Kenny Roote29df162012-08-10 08:28:37 -0700399 throw new KeyStoreException("Couldn't put certificate chain in keystore");
400 }
401 }
402
Alex Klyubinbaf28382015-03-26 14:46:55 -0700403 private void setSecretKeyEntry(String entryAlias, SecretKey key, KeyStoreParameter params)
404 throws KeyStoreException {
405 if (key instanceof KeyStoreSecretKey) {
406 // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot
407 // overwrite its own entry.
408 String keyAliasInKeystore = ((KeyStoreSecretKey) key).getAlias();
409 if (keyAliasInKeystore == null) {
410 throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
411 }
412 if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
413 throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
414 + keyAliasInKeystore);
415 }
416 String keyEntryAlias =
417 keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
418 if (!entryAlias.equals(keyEntryAlias)) {
419 throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
420 + " alias: " + entryAlias + " != " + keyEntryAlias);
421 }
422 // This is the entry where this key is already stored. No need to do anything.
423 if (params != null) {
424 throw new KeyStoreException("Modifying KeyStore-backed key using protection"
425 + " parameters not supported");
426 }
427 return;
428 }
429
430 if (params == null) {
431 throw new KeyStoreException(
432 "Protection parameters must be specified when importing a symmetric key");
433 }
434
435 // Not a KeyStore-backed secret key -- import its key material into keystore.
436 String keyExportFormat = key.getFormat();
437 if (keyExportFormat == null) {
438 throw new KeyStoreException(
439 "Only secret keys that export their key material are supported");
440 } else if (!"RAW".equals(keyExportFormat)) {
441 throw new KeyStoreException(
442 "Unsupported secret key material export format: " + keyExportFormat);
443 }
444 byte[] keyMaterial = key.getEncoded();
445 if (keyMaterial == null) {
446 throw new KeyStoreException("Key did not export its key material despite supporting"
447 + " RAW format export");
448 }
449
450 String keyAlgorithmString = key.getAlgorithm();
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700451 int keymasterAlgorithm;
452 int keymasterDigest;
Alex Klyubinbaf28382015-03-26 14:46:55 -0700453 try {
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700454 keymasterAlgorithm = KeymasterUtils.getKeymasterAlgorithmFromJcaSecretKeyAlgorithm(
455 keyAlgorithmString);
456 keymasterDigest =
457 KeymasterUtils.getKeymasterDigestfromJcaSecretKeyAlgorithm(keyAlgorithmString);
Alex Klyubinbaf28382015-03-26 14:46:55 -0700458 } catch (IllegalArgumentException e) {
459 throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
460 }
461
Alex Klyubinbaf28382015-03-26 14:46:55 -0700462 KeymasterArguments args = new KeymasterArguments();
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700463 args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);
Alex Klyubinbaf28382015-03-26 14:46:55 -0700464
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700465 int[] keymasterDigests;
Alex Klyubinc46e9e72015-04-06 15:36:25 -0700466 if (params.isDigestsSpecified()) {
467 // Digest(s) specified in parameters
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700468 keymasterDigests =
469 KeymasterUtils.getKeymasterDigestsFromJcaDigestAlgorithms(params.getDigests());
470 if (keymasterDigest != -1) {
Alex Klyubinc46e9e72015-04-06 15:36:25 -0700471 // Digest also specified in the JCA key algorithm name.
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700472 if (!com.android.internal.util.ArrayUtils.contains(
473 keymasterDigests, keymasterDigest)) {
Alex Klyubinc46e9e72015-04-06 15:36:25 -0700474 throw new KeyStoreException("Key digest mismatch"
475 + ". Key: " + keyAlgorithmString
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700476 + ", parameter spec: " + Arrays.asList(params.getDigests()));
Alex Klyubinbaf28382015-03-26 14:46:55 -0700477 }
478 }
479 } else {
Alex Klyubinc46e9e72015-04-06 15:36:25 -0700480 // No digest specified in parameters
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700481 if (keymasterDigest != -1) {
Alex Klyubinc46e9e72015-04-06 15:36:25 -0700482 // Digest specified in the JCA key algorithm name.
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700483 keymasterDigests = new int[] {keymasterDigest};
Alex Klyubinc46e9e72015-04-06 15:36:25 -0700484 } else {
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700485 keymasterDigests = EmptyArray.INT;
Alex Klyubinc46e9e72015-04-06 15:36:25 -0700486 }
Alex Klyubinbaf28382015-03-26 14:46:55 -0700487 }
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700488 args.addInts(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests);
489 if (keymasterDigests.length > 0) {
Alex Klyubinc46e9e72015-04-06 15:36:25 -0700490 // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
491 // This code will blow up if mode than one digest is specified.
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700492 int digestOutputSizeBytes =
493 KeymasterUtils.getDigestOutputSizeBytes(keymasterDigests[0]);
494 if (digestOutputSizeBytes != -1) {
Alex Klyubin4ab8ea42015-03-27 16:53:44 -0700495 // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster
496 args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
497 }
498 }
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700499 if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
500 if (keymasterDigests.length == 0) {
Alex Klyubinc46e9e72015-04-06 15:36:25 -0700501 throw new KeyStoreException("At least one digest algorithm must be specified"
502 + " for key algorithm " + keyAlgorithmString);
Alex Klyubinb406f242015-03-31 13:39:38 -0700503 }
504 }
Alex Klyubinbaf28382015-03-26 14:46:55 -0700505
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700506 @KeyStoreKeyProperties.PurposeEnum int purposes = params.getPurposes();
507 int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes(
508 params.getBlockModes());
509 if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
Alex Klyubinf853f642015-04-08 13:36:22 -0700510 && (params.isRandomizedEncryptionRequired())) {
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700511 for (int keymasterBlockMode : keymasterBlockModes) {
512 if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) {
513 throw new KeyStoreException(
514 "Randomized encryption (IND-CPA) required but may be violated by block"
515 + " mode: "
516 + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode(
517 keymasterBlockMode)
518 + ". See KeyStoreParameter documentation.");
519 }
Alex Klyubinf853f642015-04-08 13:36:22 -0700520 }
521 }
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700522 for (int keymasterPurpose : KeyStoreKeyProperties.Purpose.allToKeymaster(purposes)) {
Alex Klyubinbaf28382015-03-26 14:46:55 -0700523 args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
524 }
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700525 args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
526 int[] keymasterPaddings = ArrayUtils.concat(
527 KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings(
528 params.getEncryptionPaddings()),
529 KeymasterUtils.getKeymasterPaddingsFromJcaSignaturePaddings(
530 params.getSignaturePaddings()));
531 args.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
Alex Klyubinc46e9e72015-04-06 15:36:25 -0700532 if (params.getUserAuthenticators() == 0) {
Alex Klyubinbaf28382015-03-26 14:46:55 -0700533 args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
534 } else {
Alex Klyubinc8e55742015-03-31 19:50:13 -0700535 args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700536 KeyStoreKeyProperties.UserAuthenticator.allToKeymaster(
Alex Klyubinc8e55742015-03-31 19:50:13 -0700537 params.getUserAuthenticators()));
Alex Klyubin10a9f172015-04-16 13:41:19 -0700538 long secureUserId = GateKeeper.getSecureUserId();
539 if (secureUserId == 0) {
540 throw new IllegalStateException("Secure lock screen must be enabled"
541 + " to import keys requiring user authentication");
542 }
543 args.addLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, secureUserId);
Alex Klyubinbaf28382015-03-26 14:46:55 -0700544 }
Alex Klyubin2ea13d42015-04-01 14:41:28 -0700545 if (params.isInvalidatedOnNewFingerprintEnrolled()) {
546 // TODO: Add the invalidate on fingerprint enrolled constraint once Keymaster supports
547 // that.
548 }
Alex Klyubinc46e9e72015-04-06 15:36:25 -0700549 if (params.getUserAuthenticationValidityDurationSeconds() != -1) {
Alex Klyubinbaf28382015-03-26 14:46:55 -0700550 args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
551 params.getUserAuthenticationValidityDurationSeconds());
552 }
Alex Klyubin5045b712015-03-31 20:19:54 -0700553 args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
554 (params.getKeyValidityStart() != null)
555 ? params.getKeyValidityStart() : new Date(0));
556 args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
557 (params.getKeyValidityForOriginationEnd() != null)
558 ? params.getKeyValidityForOriginationEnd() : new Date(Long.MAX_VALUE));
559 args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
560 (params.getKeyValidityForConsumptionEnd() != null)
561 ? params.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
Alex Klyubinbaf28382015-03-26 14:46:55 -0700562
563 // TODO: Remove this once keymaster does not require us to specify the size of imported key.
564 args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8);
565
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700566 if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
Alex Klyubinf853f642015-04-08 13:36:22 -0700567 && (!params.isRandomizedEncryptionRequired())) {
568 // Permit caller-provided IV when encrypting with this key
Alex Klyubinb406f242015-03-31 13:39:38 -0700569 args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
570 }
571
Alex Klyubinbaf28382015-03-26 14:46:55 -0700572 Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias);
573 String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
574 int errorCode = mKeyStore.importKey(
575 keyAliasInKeystore,
576 args,
577 KeymasterDefs.KM_KEY_FORMAT_RAW,
578 keyMaterial,
579 params.getFlags(),
580 new KeyCharacteristics());
581 if (errorCode != android.security.KeyStore.NO_ERROR) {
582 throw new KeyStoreException("Failed to import secret key. Keystore error code: "
583 + errorCode);
584 }
585 }
586
Kenny Roote29df162012-08-10 08:28:37 -0700587 @Override
588 public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
589 throws KeyStoreException {
Kenny Roota4640c02012-08-31 13:38:11 -0700590 throw new KeyStoreException("Operation not supported because key encoding is unknown");
Kenny Roote29df162012-08-10 08:28:37 -0700591 }
592
593 @Override
594 public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
595 if (isKeyEntry(alias)) {
596 throw new KeyStoreException("Entry exists and is not a trusted certificate");
597 }
598
Kenny Roota4640c02012-08-31 13:38:11 -0700599 // We can't set something to null.
600 if (cert == null) {
601 throw new NullPointerException("cert == null");
602 }
603
Kenny Roote29df162012-08-10 08:28:37 -0700604 final byte[] encoded;
605 try {
606 encoded = cert.getEncoded();
607 } catch (CertificateEncodingException e) {
608 throw new KeyStoreException(e);
609 }
610
Kenny Root2eeda722013-04-10 11:30:58 -0700611 if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded,
612 android.security.KeyStore.UID_SELF, android.security.KeyStore.FLAG_NONE)) {
Kenny Roote29df162012-08-10 08:28:37 -0700613 throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
614 }
615 }
616
617 @Override
618 public void engineDeleteEntry(String alias) throws KeyStoreException {
Kenny Roota4640c02012-08-31 13:38:11 -0700619 if (!isKeyEntry(alias) && !isCertificateEntry(alias)) {
620 return;
621 }
622
Kenny Rootdb026712012-08-20 10:48:46 -0700623 if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
Kenny Roote29df162012-08-10 08:28:37 -0700624 throw new KeyStoreException("No such entry " + alias);
625 }
626 }
627
Kenny Roote29df162012-08-10 08:28:37 -0700628 private Set<String> getUniqueAliases() {
629 final String[] rawAliases = mKeyStore.saw("");
630 if (rawAliases == null) {
631 return new HashSet<String>();
632 }
633
634 final Set<String> aliases = new HashSet<String>(rawAliases.length);
635 for (String alias : rawAliases) {
636 final int idx = alias.indexOf('_');
637 if ((idx == -1) || (alias.length() <= idx)) {
638 Log.e(NAME, "invalid alias: " + alias);
639 continue;
640 }
641
642 aliases.add(new String(alias.substring(idx + 1)));
643 }
644
645 return aliases;
646 }
647
648 @Override
649 public Enumeration<String> engineAliases() {
650 return Collections.enumeration(getUniqueAliases());
651 }
652
653 @Override
654 public boolean engineContainsAlias(String alias) {
Kenny Roota4640c02012-08-31 13:38:11 -0700655 if (alias == null) {
656 throw new NullPointerException("alias == null");
657 }
658
Kenny Roote29df162012-08-10 08:28:37 -0700659 return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias)
Alex Klyubinbaf28382015-03-26 14:46:55 -0700660 || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias)
Kenny Roote29df162012-08-10 08:28:37 -0700661 || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias)
662 || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
663 }
664
665 @Override
666 public int engineSize() {
667 return getUniqueAliases().size();
668 }
669
670 @Override
671 public boolean engineIsKeyEntry(String alias) {
672 return isKeyEntry(alias);
673 }
674
675 private boolean isKeyEntry(String alias) {
Alex Klyubinbaf28382015-03-26 14:46:55 -0700676 return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias);
677 }
678
679 private boolean isPrivateKeyEntry(String alias) {
Kenny Roota4640c02012-08-31 13:38:11 -0700680 if (alias == null) {
681 throw new NullPointerException("alias == null");
682 }
683
Kenny Roote29df162012-08-10 08:28:37 -0700684 return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias);
685 }
686
Alex Klyubinbaf28382015-03-26 14:46:55 -0700687 private boolean isSecretKeyEntry(String alias) {
688 if (alias == null) {
689 throw new NullPointerException("alias == null");
690 }
691
692 return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias);
693 }
694
Kenny Roota4640c02012-08-31 13:38:11 -0700695 private boolean isCertificateEntry(String alias) {
696 if (alias == null) {
697 throw new NullPointerException("alias == null");
698 }
699
700 return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
701 }
702
Kenny Roote29df162012-08-10 08:28:37 -0700703 @Override
704 public boolean engineIsCertificateEntry(String alias) {
Kenny Roota4640c02012-08-31 13:38:11 -0700705 return !isKeyEntry(alias) && isCertificateEntry(alias);
Kenny Roote29df162012-08-10 08:28:37 -0700706 }
707
708 @Override
709 public String engineGetCertificateAlias(Certificate cert) {
710 if (cert == null) {
711 return null;
712 }
713
714 final Set<String> nonCaEntries = new HashSet<String>();
715
716 /*
717 * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation
718 * says to only compare the first certificate in the chain which is
719 * equivalent to the USER_CERTIFICATE prefix for the Android keystore
720 * convention.
721 */
722 final String[] certAliases = mKeyStore.saw(Credentials.USER_CERTIFICATE);
Kenny Root78ad8492013-02-13 17:02:57 -0800723 if (certAliases != null) {
724 for (String alias : certAliases) {
725 final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
726 if (certBytes == null) {
727 continue;
728 }
Kenny Roote29df162012-08-10 08:28:37 -0700729
Kenny Root78ad8492013-02-13 17:02:57 -0800730 final Certificate c = toCertificate(certBytes);
731 nonCaEntries.add(alias);
Kenny Roote29df162012-08-10 08:28:37 -0700732
Kenny Root78ad8492013-02-13 17:02:57 -0800733 if (cert.equals(c)) {
734 return alias;
735 }
Kenny Roote29df162012-08-10 08:28:37 -0700736 }
737 }
738
739 /*
740 * Look at all the TrustedCertificateEntry types. Skip all the
741 * PrivateKeyEntry we looked at above.
742 */
743 final String[] caAliases = mKeyStore.saw(Credentials.CA_CERTIFICATE);
Kenny Root78ad8492013-02-13 17:02:57 -0800744 if (certAliases != null) {
745 for (String alias : caAliases) {
746 if (nonCaEntries.contains(alias)) {
747 continue;
748 }
Kenny Roote29df162012-08-10 08:28:37 -0700749
Kenny Root78ad8492013-02-13 17:02:57 -0800750 final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
751 if (certBytes == null) {
752 continue;
753 }
Kenny Roote29df162012-08-10 08:28:37 -0700754
Kenny Root78ad8492013-02-13 17:02:57 -0800755 final Certificate c =
756 toCertificate(mKeyStore.get(Credentials.CA_CERTIFICATE + alias));
757 if (cert.equals(c)) {
758 return alias;
759 }
Kenny Roote29df162012-08-10 08:28:37 -0700760 }
761 }
762
763 return null;
764 }
765
766 @Override
767 public void engineStore(OutputStream stream, char[] password) throws IOException,
768 NoSuchAlgorithmException, CertificateException {
769 throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
770 }
771
772 @Override
773 public void engineLoad(InputStream stream, char[] password) throws IOException,
774 NoSuchAlgorithmException, CertificateException {
775 if (stream != null) {
776 throw new IllegalArgumentException("InputStream not supported");
777 }
778
779 if (password != null) {
780 throw new IllegalArgumentException("password not supported");
781 }
782
783 // Unfortunate name collision.
784 mKeyStore = android.security.KeyStore.getInstance();
785 }
786
Kenny Root2eeda722013-04-10 11:30:58 -0700787 @Override
788 public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
789 throws KeyStoreException {
790 if (entry == null) {
791 throw new KeyStoreException("entry == null");
792 }
793
794 if (engineContainsAlias(alias)) {
795 engineDeleteEntry(alias);
796 }
797
798 if (entry instanceof KeyStore.TrustedCertificateEntry) {
799 KeyStore.TrustedCertificateEntry trE = (KeyStore.TrustedCertificateEntry) entry;
800 engineSetCertificateEntry(alias, trE.getTrustedCertificate());
801 return;
802 }
803
Kenny Root1c219f62013-04-18 17:57:03 -0700804 if (param != null && !(param instanceof KeyStoreParameter)) {
805 throw new KeyStoreException(
806 "protParam should be android.security.KeyStoreParameter; was: "
Kenny Root2eeda722013-04-10 11:30:58 -0700807 + param.getClass().getName());
808 }
809
810 if (entry instanceof PrivateKeyEntry) {
811 PrivateKeyEntry prE = (PrivateKeyEntry) entry;
812 setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(),
Kenny Root1c219f62013-04-18 17:57:03 -0700813 (KeyStoreParameter) param);
Alex Klyubinbaf28382015-03-26 14:46:55 -0700814 } else if (entry instanceof SecretKeyEntry) {
815 SecretKeyEntry secE = (SecretKeyEntry) entry;
816 setSecretKeyEntry(alias, secE.getSecretKey(), (KeyStoreParameter) param);
817 } else {
818 throw new KeyStoreException(
819 "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
820 + "; was " + entry);
Kenny Root2eeda722013-04-10 11:30:58 -0700821 }
Kenny Root2eeda722013-04-10 11:30:58 -0700822 }
823
Kenny Roote29df162012-08-10 08:28:37 -0700824}