• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.security.keystore;
18 
19 import libcore.util.EmptyArray;
20 import android.security.Credentials;
21 import android.security.KeyStore;
22 import android.security.KeyStoreParameter;
23 import android.security.keymaster.KeyCharacteristics;
24 import android.security.keymaster.KeymasterArguments;
25 import android.security.keymaster.KeymasterDefs;
26 import android.security.keystore.KeyProperties;
27 import android.security.keystore.KeyProtection;
28 import android.util.Log;
29 
30 import java.io.ByteArrayInputStream;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.OutputStream;
34 import java.security.Key;
35 import java.security.KeyStore.Entry;
36 import java.security.KeyStore.LoadStoreParameter;
37 import java.security.KeyStore.PrivateKeyEntry;
38 import java.security.KeyStore.ProtectionParameter;
39 import java.security.KeyStore.SecretKeyEntry;
40 import java.security.KeyStoreException;
41 import java.security.KeyStoreSpi;
42 import java.security.NoSuchAlgorithmException;
43 import java.security.PrivateKey;
44 import java.security.ProviderException;
45 import java.security.PublicKey;
46 import java.security.UnrecoverableKeyException;
47 import java.security.cert.Certificate;
48 import java.security.cert.CertificateEncodingException;
49 import java.security.cert.CertificateException;
50 import java.security.cert.CertificateFactory;
51 import java.security.cert.X509Certificate;
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.Collection;
55 import java.util.Collections;
56 import java.util.Date;
57 import java.util.Enumeration;
58 import java.util.HashSet;
59 import java.util.Iterator;
60 import java.util.Set;
61 
62 import javax.crypto.SecretKey;
63 
64 /**
65  * A java.security.KeyStore interface for the Android KeyStore. An instance of
66  * it can be created via the {@link java.security.KeyStore#getInstance(String)
67  * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a
68  * java.security.KeyStore backed by this "AndroidKeyStore" implementation.
69  * <p>
70  * This is built on top of Android's keystore daemon. The convention of alias
71  * use is:
72  * <p>
73  * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key,
74  * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one
75  * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE
76  * entry which will have the rest of the chain concatenated in BER format.
77  * <p>
78  * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry
79  * with a single certificate.
80  *
81  * @hide
82  */
83 public class AndroidKeyStoreSpi extends KeyStoreSpi {
84     public static final String NAME = "AndroidKeyStore";
85 
86     private KeyStore mKeyStore;
87     private int mUid = KeyStore.UID_SELF;
88 
89     @Override
engineGetKey(String alias, char[] password)90     public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
91             UnrecoverableKeyException {
92         if (isPrivateKeyEntry(alias)) {
93             String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
94             return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
95                     mKeyStore, privateKeyAlias, mUid);
96         } else if (isSecretKeyEntry(alias)) {
97             String secretKeyAlias = Credentials.USER_SECRET_KEY + alias;
98             return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(
99                     mKeyStore, secretKeyAlias, mUid);
100         } else {
101             // Key not found
102             return null;
103         }
104     }
105 
106     @Override
engineGetCertificateChain(String alias)107     public Certificate[] engineGetCertificateChain(String alias) {
108         if (alias == null) {
109             throw new NullPointerException("alias == null");
110         }
111 
112         final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias);
113         if (leaf == null) {
114             return null;
115         }
116 
117         final Certificate[] caList;
118 
119         final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
120         if (caBytes != null) {
121             final Collection<X509Certificate> caChain = toCertificates(caBytes);
122 
123             caList = new Certificate[caChain.size() + 1];
124 
125             final Iterator<X509Certificate> it = caChain.iterator();
126             int i = 1;
127             while (it.hasNext()) {
128                 caList[i++] = it.next();
129             }
130         } else {
131             caList = new Certificate[1];
132         }
133 
134         caList[0] = leaf;
135 
136         return caList;
137     }
138 
139     @Override
engineGetCertificate(String alias)140     public Certificate engineGetCertificate(String alias) {
141         if (alias == null) {
142             throw new NullPointerException("alias == null");
143         }
144 
145         byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
146         if (encodedCert != null) {
147             return getCertificateForPrivateKeyEntry(alias, encodedCert);
148         }
149 
150         encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
151         if (encodedCert != null) {
152             return getCertificateForTrustedCertificateEntry(encodedCert);
153         }
154 
155         // This entry/alias does not contain a certificate.
156         return null;
157     }
158 
getCertificateForTrustedCertificateEntry(byte[] encodedCert)159     private Certificate getCertificateForTrustedCertificateEntry(byte[] encodedCert) {
160         // For this certificate there shouldn't be a private key in this KeyStore entry. Thus,
161         // there's no need to wrap this certificate as opposed to the certificate associated with
162         // a private key entry.
163         return toCertificate(encodedCert);
164     }
165 
getCertificateForPrivateKeyEntry(String alias, byte[] encodedCert)166     private Certificate getCertificateForPrivateKeyEntry(String alias, byte[] encodedCert) {
167         // All crypto algorithms offered by Android Keystore for its private keys must also
168         // be offered for the corresponding public keys stored in the Android Keystore. The
169         // complication is that the underlying keystore service operates only on full key pairs,
170         // rather than just public keys or private keys. As a result, Android Keystore-backed
171         // crypto can only be offered for public keys for which keystore contains the
172         // corresponding private key. This is not the case for certificate-only entries (e.g.,
173         // trusted certificates).
174         //
175         // getCertificate().getPublicKey() is the only way to obtain the public key
176         // corresponding to the private key stored in the KeyStore. Thus, we need to make sure
177         // that the returned public key points to the underlying key pair / private key
178         // when available.
179 
180         X509Certificate cert = toCertificate(encodedCert);
181         if (cert == null) {
182             // Failed to parse the certificate.
183             return null;
184         }
185 
186         String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
187         if (mKeyStore.contains(privateKeyAlias, mUid)) {
188             // As expected, keystore contains the private key corresponding to this public key. Wrap
189             // the certificate so that its getPublicKey method returns an Android Keystore
190             // PublicKey. This key will delegate crypto operations involving this public key to
191             // Android Keystore when higher-priority providers do not offer these crypto
192             // operations for this key.
193             return wrapIntoKeyStoreCertificate(privateKeyAlias, mUid, cert);
194         } else {
195             // This KeyStore entry/alias is supposed to contain the private key corresponding to
196             // the public key in this certificate, but it does not for some reason. It's probably a
197             // bug. Let other providers handle crypto operations involving the public key returned
198             // by this certificate's getPublicKey.
199             return cert;
200         }
201     }
202 
203     /**
204      * Wraps the provided cerificate into {@link KeyStoreX509Certificate} so that the public key
205      * returned by the certificate contains information about the alias of the private key in
206      * keystore. This is needed so that Android Keystore crypto operations using public keys can
207      * find out which key alias to use. These operations cannot work without an alias.
208      */
wrapIntoKeyStoreCertificate( String privateKeyAlias, int uid, X509Certificate certificate)209     private static KeyStoreX509Certificate wrapIntoKeyStoreCertificate(
210             String privateKeyAlias, int uid, X509Certificate certificate) {
211         return (certificate != null)
212                 ? new KeyStoreX509Certificate(privateKeyAlias, uid, certificate) : null;
213     }
214 
toCertificate(byte[] bytes)215     private static X509Certificate toCertificate(byte[] bytes) {
216         try {
217             final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
218             return (X509Certificate) certFactory.generateCertificate(
219                     new ByteArrayInputStream(bytes));
220         } catch (CertificateException e) {
221             Log.w(NAME, "Couldn't parse certificate in keystore", e);
222             return null;
223         }
224     }
225 
226     @SuppressWarnings("unchecked")
toCertificates(byte[] bytes)227     private static Collection<X509Certificate> toCertificates(byte[] bytes) {
228         try {
229             final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
230             return (Collection<X509Certificate>) certFactory.generateCertificates(
231                             new ByteArrayInputStream(bytes));
232         } catch (CertificateException e) {
233             Log.w(NAME, "Couldn't parse certificates in keystore", e);
234             return new ArrayList<X509Certificate>();
235         }
236     }
237 
getModificationDate(String alias)238     private Date getModificationDate(String alias) {
239         final long epochMillis = mKeyStore.getmtime(alias, mUid);
240         if (epochMillis == -1L) {
241             return null;
242         }
243 
244         return new Date(epochMillis);
245     }
246 
247     @Override
engineGetCreationDate(String alias)248     public Date engineGetCreationDate(String alias) {
249         if (alias == null) {
250             throw new NullPointerException("alias == null");
251         }
252 
253         Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias);
254         if (d != null) {
255             return d;
256         }
257 
258         d = getModificationDate(Credentials.USER_SECRET_KEY + alias);
259         if (d != null) {
260             return d;
261         }
262 
263         d = getModificationDate(Credentials.USER_CERTIFICATE + alias);
264         if (d != null) {
265             return d;
266         }
267 
268         return getModificationDate(Credentials.CA_CERTIFICATE + alias);
269     }
270 
271     @Override
engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)272     public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
273             throws KeyStoreException {
274         if ((password != null) && (password.length > 0)) {
275             throw new KeyStoreException("entries cannot be protected with passwords");
276         }
277 
278         if (key instanceof PrivateKey) {
279             setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
280         } else if (key instanceof SecretKey) {
281             setSecretKeyEntry(alias, (SecretKey) key, null);
282         } else {
283             throw new KeyStoreException("Only PrivateKey and SecretKey are supported");
284         }
285     }
286 
getLegacyKeyProtectionParameter(PrivateKey key)287     private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key)
288             throws KeyStoreException {
289         String keyAlgorithm = key.getAlgorithm();
290         KeyProtection.Builder specBuilder;
291         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
292             specBuilder =
293                     new KeyProtection.Builder(
294                             KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
295             // Authorized to be used with any digest (including no digest).
296             // MD5 was never offered for Android Keystore for ECDSA.
297             specBuilder.setDigests(
298                     KeyProperties.DIGEST_NONE,
299                     KeyProperties.DIGEST_SHA1,
300                     KeyProperties.DIGEST_SHA224,
301                     KeyProperties.DIGEST_SHA256,
302                     KeyProperties.DIGEST_SHA384,
303                     KeyProperties.DIGEST_SHA512);
304         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
305             specBuilder =
306                     new KeyProtection.Builder(
307                             KeyProperties.PURPOSE_ENCRYPT
308                             | KeyProperties.PURPOSE_DECRYPT
309                             | KeyProperties.PURPOSE_SIGN
310                             | KeyProperties.PURPOSE_VERIFY);
311             // Authorized to be used with any digest (including no digest).
312             specBuilder.setDigests(
313                     KeyProperties.DIGEST_NONE,
314                     KeyProperties.DIGEST_MD5,
315                     KeyProperties.DIGEST_SHA1,
316                     KeyProperties.DIGEST_SHA224,
317                     KeyProperties.DIGEST_SHA256,
318                     KeyProperties.DIGEST_SHA384,
319                     KeyProperties.DIGEST_SHA512);
320             // Authorized to be used with any encryption and signature padding
321             // schemes (including no padding).
322             specBuilder.setEncryptionPaddings(
323                     KeyProperties.ENCRYPTION_PADDING_NONE,
324                     KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
325                     KeyProperties.ENCRYPTION_PADDING_RSA_OAEP);
326             specBuilder.setSignaturePaddings(
327                     KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
328                     KeyProperties.SIGNATURE_PADDING_RSA_PSS);
329             // Disable randomized encryption requirement to support encryption
330             // padding NONE above.
331             specBuilder.setRandomizedEncryptionRequired(false);
332         } else {
333             throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
334         }
335         specBuilder.setUserAuthenticationRequired(false);
336 
337         return specBuilder.build();
338     }
339 
setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain, java.security.KeyStore.ProtectionParameter param)340     private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
341             java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
342         int flags = 0;
343         KeyProtection spec;
344         if (param == null) {
345             spec = getLegacyKeyProtectionParameter(key);
346         } else if (param instanceof KeyStoreParameter) {
347             spec = getLegacyKeyProtectionParameter(key);
348             KeyStoreParameter legacySpec = (KeyStoreParameter) param;
349             if (legacySpec.isEncryptionRequired()) {
350                 flags = KeyStore.FLAG_ENCRYPTED;
351             }
352         } else if (param instanceof KeyProtection) {
353             spec = (KeyProtection) param;
354         } else {
355             throw new KeyStoreException(
356                     "Unsupported protection parameter class:" + param.getClass().getName()
357                     + ". Supported: " + KeyProtection.class.getName() + ", "
358                     + KeyStoreParameter.class.getName());
359         }
360 
361         // Make sure the chain exists since this is a PrivateKey
362         if ((chain == null) || (chain.length == 0)) {
363             throw new KeyStoreException("Must supply at least one Certificate with PrivateKey");
364         }
365 
366         // Do chain type checking.
367         X509Certificate[] x509chain = new X509Certificate[chain.length];
368         for (int i = 0; i < chain.length; i++) {
369             if (!"X.509".equals(chain[i].getType())) {
370                 throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
371                         + i);
372             }
373 
374             if (!(chain[i] instanceof X509Certificate)) {
375                 throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
376                         + i);
377             }
378 
379             x509chain[i] = (X509Certificate) chain[i];
380         }
381 
382         final byte[] userCertBytes;
383         try {
384             userCertBytes = x509chain[0].getEncoded();
385         } catch (CertificateEncodingException e) {
386             throw new KeyStoreException("Failed to encode certificate #0", e);
387         }
388 
389         /*
390          * If we have a chain, store it in the CA certificate slot for this
391          * alias as concatenated DER-encoded certificates. These can be
392          * deserialized by {@link CertificateFactory#generateCertificates}.
393          */
394         final byte[] chainBytes;
395         if (chain.length > 1) {
396             /*
397              * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...}
398              * so we only need the certificates starting at index 1.
399              */
400             final byte[][] certsBytes = new byte[x509chain.length - 1][];
401             int totalCertLength = 0;
402             for (int i = 0; i < certsBytes.length; i++) {
403                 try {
404                     certsBytes[i] = x509chain[i + 1].getEncoded();
405                     totalCertLength += certsBytes[i].length;
406                 } catch (CertificateEncodingException e) {
407                     throw new KeyStoreException("Failed to encode certificate #" + i, e);
408                 }
409             }
410 
411             /*
412              * Serialize this into one byte array so we can later call
413              * CertificateFactory#generateCertificates to recover them.
414              */
415             chainBytes = new byte[totalCertLength];
416             int outputOffset = 0;
417             for (int i = 0; i < certsBytes.length; i++) {
418                 final int certLength = certsBytes[i].length;
419                 System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength);
420                 outputOffset += certLength;
421                 certsBytes[i] = null;
422             }
423         } else {
424             chainBytes = null;
425         }
426 
427         final String pkeyAlias;
428         if (key instanceof AndroidKeyStorePrivateKey) {
429             pkeyAlias = ((AndroidKeyStoreKey) key).getAlias();
430         } else {
431             pkeyAlias = null;
432         }
433 
434         byte[] pkcs8EncodedPrivateKeyBytes;
435         KeymasterArguments importArgs;
436         final boolean shouldReplacePrivateKey;
437         if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
438             final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
439             if (!alias.equals(keySubalias)) {
440                 throw new KeyStoreException("Can only replace keys with same alias: " + alias
441                         + " != " + keySubalias);
442             }
443             shouldReplacePrivateKey = false;
444             importArgs = null;
445             pkcs8EncodedPrivateKeyBytes = null;
446         } else {
447             shouldReplacePrivateKey = true;
448             // Make sure the PrivateKey format is the one we support.
449             final String keyFormat = key.getFormat();
450             if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
451                 throw new KeyStoreException(
452                         "Unsupported private key export format: " + keyFormat
453                         + ". Only private keys which export their key material in PKCS#8 format are"
454                         + " supported.");
455             }
456 
457             // Make sure we can actually encode the key.
458             pkcs8EncodedPrivateKeyBytes = key.getEncoded();
459             if (pkcs8EncodedPrivateKeyBytes == null) {
460                 throw new KeyStoreException("Private key did not export any key material");
461             }
462 
463             importArgs = new KeymasterArguments();
464             try {
465                 importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM,
466                         KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
467                                 key.getAlgorithm()));
468                 @KeyProperties.PurposeEnum int purposes = spec.getPurposes();
469                 importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
470                         KeyProperties.Purpose.allToKeymaster(purposes));
471                 if (spec.isDigestsSpecified()) {
472                     importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST,
473                             KeyProperties.Digest.allToKeymaster(spec.getDigests()));
474                 }
475 
476                 importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE,
477                         KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()));
478                 int[] keymasterEncryptionPaddings =
479                         KeyProperties.EncryptionPadding.allToKeymaster(
480                                 spec.getEncryptionPaddings());
481                 if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
482                         && (spec.isRandomizedEncryptionRequired())) {
483                     for (int keymasterPadding : keymasterEncryptionPaddings) {
484                         if (!KeymasterUtils
485                                 .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
486                                         keymasterPadding)) {
487                             throw new KeyStoreException(
488                                     "Randomized encryption (IND-CPA) required but is violated by"
489                                     + " encryption padding mode: "
490                                     + KeyProperties.EncryptionPadding.fromKeymaster(
491                                             keymasterPadding)
492                                     + ". See KeyProtection documentation.");
493                         }
494                     }
495                 }
496                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
497                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
498                         KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
499                 KeymasterUtils.addUserAuthArgs(importArgs,
500                         spec.isUserAuthenticationRequired(),
501                         spec.getUserAuthenticationValidityDurationSeconds(),
502                         spec.isUserAuthenticationValidWhileOnBody(),
503                         spec.isInvalidatedByBiometricEnrollment());
504                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
505                         spec.getKeyValidityStart());
506                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
507                         spec.getKeyValidityForOriginationEnd());
508                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
509                         spec.getKeyValidityForConsumptionEnd());
510             } catch (IllegalArgumentException | IllegalStateException e) {
511                 throw new KeyStoreException(e);
512             }
513         }
514 
515 
516         boolean success = false;
517         try {
518             // Store the private key, if necessary
519             if (shouldReplacePrivateKey) {
520                 // Delete the stored private key and any related entries before importing the
521                 // provided key
522                 Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
523                 KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
524                 int errorCode = mKeyStore.importKey(
525                         Credentials.USER_PRIVATE_KEY + alias,
526                         importArgs,
527                         KeymasterDefs.KM_KEY_FORMAT_PKCS8,
528                         pkcs8EncodedPrivateKeyBytes,
529                         mUid,
530                         flags,
531                         resultingKeyCharacteristics);
532                 if (errorCode != KeyStore.NO_ERROR) {
533                     throw new KeyStoreException("Failed to store private key",
534                             KeyStore.getKeyStoreException(errorCode));
535                 }
536             } else {
537                 // Keep the stored private key around -- delete all other entry types
538                 Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
539                 Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
540             }
541 
542             // Store the leaf certificate
543             int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes,
544                     mUid, flags);
545             if (errorCode != KeyStore.NO_ERROR) {
546                 throw new KeyStoreException("Failed to store certificate #0",
547                         KeyStore.getKeyStoreException(errorCode));
548             }
549 
550             // Store the certificate chain
551             errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes,
552                     mUid, flags);
553             if (errorCode != KeyStore.NO_ERROR) {
554                 throw new KeyStoreException("Failed to store certificate chain",
555                         KeyStore.getKeyStoreException(errorCode));
556             }
557             success = true;
558         } finally {
559             if (!success) {
560                 if (shouldReplacePrivateKey) {
561                     Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
562                 } else {
563                     Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
564                     Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
565                 }
566             }
567         }
568     }
569 
setSecretKeyEntry(String entryAlias, SecretKey key, java.security.KeyStore.ProtectionParameter param)570     private void setSecretKeyEntry(String entryAlias, SecretKey key,
571             java.security.KeyStore.ProtectionParameter param)
572             throws KeyStoreException {
573         if ((param != null) && (!(param instanceof KeyProtection))) {
574             throw new KeyStoreException(
575                     "Unsupported protection parameter class: " + param.getClass().getName()
576                     + ". Supported: " + KeyProtection.class.getName());
577         }
578         KeyProtection params = (KeyProtection) param;
579 
580         if (key instanceof AndroidKeyStoreSecretKey) {
581             // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot
582             // overwrite its own entry.
583             String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias();
584             if (keyAliasInKeystore == null) {
585                 throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
586             }
587             if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
588                 throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
589                         + keyAliasInKeystore);
590             }
591             String keyEntryAlias =
592                     keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
593             if (!entryAlias.equals(keyEntryAlias)) {
594                 throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
595                         + " alias: " + entryAlias + " != " + keyEntryAlias);
596             }
597             // This is the entry where this key is already stored. No need to do anything.
598             if (params != null) {
599                 throw new KeyStoreException("Modifying KeyStore-backed key using protection"
600                         + " parameters not supported");
601             }
602             return;
603         }
604 
605         if (params == null) {
606             throw new KeyStoreException(
607                     "Protection parameters must be specified when importing a symmetric key");
608         }
609 
610         // Not a KeyStore-backed secret key -- import its key material into keystore.
611         String keyExportFormat = key.getFormat();
612         if (keyExportFormat == null) {
613             throw new KeyStoreException(
614                     "Only secret keys that export their key material are supported");
615         } else if (!"RAW".equals(keyExportFormat)) {
616             throw new KeyStoreException(
617                     "Unsupported secret key material export format: " + keyExportFormat);
618         }
619         byte[] keyMaterial = key.getEncoded();
620         if (keyMaterial == null) {
621             throw new KeyStoreException("Key did not export its key material despite supporting"
622                     + " RAW format export");
623         }
624 
625         KeymasterArguments args = new KeymasterArguments();
626         try {
627             int keymasterAlgorithm =
628                     KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(key.getAlgorithm());
629             args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);
630 
631             int[] keymasterDigests;
632             if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
633                 // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
634                 // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one
635                 // digest, we don't let import parameters override the digest implied by the key.
636                 // If the parameters specify digests at all, they must specify only one digest, the
637                 // only implied by key algorithm.
638                 int keymasterImpliedDigest =
639                         KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm());
640                 if (keymasterImpliedDigest == -1) {
641                     throw new ProviderException(
642                             "HMAC key algorithm digest unknown for key algorithm "
643                                     + key.getAlgorithm());
644                 }
645                 keymasterDigests = new int[] {keymasterImpliedDigest};
646                 if (params.isDigestsSpecified()) {
647                     // Digest(s) explicitly specified in params -- check that the list consists of
648                     // exactly one digest, the one implied by key algorithm.
649                     int[] keymasterDigestsFromParams =
650                             KeyProperties.Digest.allToKeymaster(params.getDigests());
651                     if ((keymasterDigestsFromParams.length != 1)
652                             || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) {
653                         throw new KeyStoreException(
654                                 "Unsupported digests specification: "
655                                 + Arrays.asList(params.getDigests()) + ". Only "
656                                 + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest)
657                                 + " supported for HMAC key algorithm " + key.getAlgorithm());
658                     }
659                 }
660             } else {
661                 // Key algorithm does not imply a digest.
662                 if (params.isDigestsSpecified()) {
663                     keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests());
664                 } else {
665                     keymasterDigests = EmptyArray.INT;
666                 }
667             }
668             args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests);
669 
670             @KeyProperties.PurposeEnum int purposes = params.getPurposes();
671             int[] keymasterBlockModes =
672                     KeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
673             if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
674                     && (params.isRandomizedEncryptionRequired())) {
675                 for (int keymasterBlockMode : keymasterBlockModes) {
676                     if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
677                             keymasterBlockMode)) {
678                         throw new KeyStoreException(
679                                 "Randomized encryption (IND-CPA) required but may be violated by"
680                                 + " block mode: "
681                                 + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
682                                 + ". See KeyProtection documentation.");
683                     }
684                 }
685             }
686             args.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
687                     KeyProperties.Purpose.allToKeymaster(purposes));
688             args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
689             if (params.getSignaturePaddings().length > 0) {
690                 throw new KeyStoreException("Signature paddings not supported for symmetric keys");
691             }
692             int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
693                     params.getEncryptionPaddings());
694             args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
695             KeymasterUtils.addUserAuthArgs(args,
696                     params.isUserAuthenticationRequired(),
697                     params.getUserAuthenticationValidityDurationSeconds(),
698                     params.isUserAuthenticationValidWhileOnBody(),
699                     params.isInvalidatedByBiometricEnrollment());
700             KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
701                     args,
702                     keymasterAlgorithm,
703                     keymasterBlockModes,
704                     keymasterDigests);
705             args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
706                     params.getKeyValidityStart());
707             args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
708                     params.getKeyValidityForOriginationEnd());
709             args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
710                     params.getKeyValidityForConsumptionEnd());
711 
712             if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
713                     && (!params.isRandomizedEncryptionRequired())) {
714                 // Permit caller-provided IV when encrypting with this key
715                 args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
716             }
717         } catch (IllegalArgumentException | IllegalStateException e) {
718             throw new KeyStoreException(e);
719         }
720 
721         Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid);
722         String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
723         int errorCode = mKeyStore.importKey(
724                 keyAliasInKeystore,
725                 args,
726                 KeymasterDefs.KM_KEY_FORMAT_RAW,
727                 keyMaterial,
728                 mUid,
729                 0, // flags
730                 new KeyCharacteristics());
731         if (errorCode != KeyStore.NO_ERROR) {
732             throw new KeyStoreException("Failed to import secret key. Keystore error code: "
733                 + errorCode);
734         }
735     }
736 
737     @Override
engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)738     public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
739             throws KeyStoreException {
740         throw new KeyStoreException("Operation not supported because key encoding is unknown");
741     }
742 
743     @Override
engineSetCertificateEntry(String alias, Certificate cert)744     public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
745         if (isKeyEntry(alias)) {
746             throw new KeyStoreException("Entry exists and is not a trusted certificate");
747         }
748 
749         // We can't set something to null.
750         if (cert == null) {
751             throw new NullPointerException("cert == null");
752         }
753 
754         final byte[] encoded;
755         try {
756             encoded = cert.getEncoded();
757         } catch (CertificateEncodingException e) {
758             throw new KeyStoreException(e);
759         }
760 
761         if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, mUid, KeyStore.FLAG_NONE)) {
762             throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
763         }
764     }
765 
766     @Override
engineDeleteEntry(String alias)767     public void engineDeleteEntry(String alias) throws KeyStoreException {
768         if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid)) {
769             throw new KeyStoreException("Failed to delete entry: " + alias);
770         }
771     }
772 
getUniqueAliases()773     private Set<String> getUniqueAliases() {
774         final String[] rawAliases = mKeyStore.list("", mUid);
775         if (rawAliases == null) {
776             return new HashSet<String>();
777         }
778 
779         final Set<String> aliases = new HashSet<String>(rawAliases.length);
780         for (String alias : rawAliases) {
781             final int idx = alias.indexOf('_');
782             if ((idx == -1) || (alias.length() <= idx)) {
783                 Log.e(NAME, "invalid alias: " + alias);
784                 continue;
785             }
786 
787             aliases.add(new String(alias.substring(idx + 1)));
788         }
789 
790         return aliases;
791     }
792 
793     @Override
engineAliases()794     public Enumeration<String> engineAliases() {
795         return Collections.enumeration(getUniqueAliases());
796     }
797 
798     @Override
engineContainsAlias(String alias)799     public boolean engineContainsAlias(String alias) {
800         if (alias == null) {
801             throw new NullPointerException("alias == null");
802         }
803 
804         return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid)
805                 || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid)
806                 || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias, mUid)
807                 || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
808     }
809 
810     @Override
engineSize()811     public int engineSize() {
812         return getUniqueAliases().size();
813     }
814 
815     @Override
engineIsKeyEntry(String alias)816     public boolean engineIsKeyEntry(String alias) {
817         return isKeyEntry(alias);
818     }
819 
isKeyEntry(String alias)820     private boolean isKeyEntry(String alias) {
821         return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias);
822     }
823 
isPrivateKeyEntry(String alias)824     private boolean isPrivateKeyEntry(String alias) {
825         if (alias == null) {
826             throw new NullPointerException("alias == null");
827         }
828 
829         return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid);
830     }
831 
isSecretKeyEntry(String alias)832     private boolean isSecretKeyEntry(String alias) {
833         if (alias == null) {
834             throw new NullPointerException("alias == null");
835         }
836 
837         return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
838     }
839 
isCertificateEntry(String alias)840     private boolean isCertificateEntry(String alias) {
841         if (alias == null) {
842             throw new NullPointerException("alias == null");
843         }
844 
845         return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
846     }
847 
848     @Override
engineIsCertificateEntry(String alias)849     public boolean engineIsCertificateEntry(String alias) {
850         return !isKeyEntry(alias) && isCertificateEntry(alias);
851     }
852 
853     @Override
engineGetCertificateAlias(Certificate cert)854     public String engineGetCertificateAlias(Certificate cert) {
855         if (cert == null) {
856             return null;
857         }
858         if (!"X.509".equalsIgnoreCase(cert.getType())) {
859             // Only X.509 certificates supported
860             return null;
861         }
862         byte[] targetCertBytes;
863         try {
864             targetCertBytes = cert.getEncoded();
865         } catch (CertificateEncodingException e) {
866             return null;
867         }
868         if (targetCertBytes == null) {
869             return null;
870         }
871 
872         final Set<String> nonCaEntries = new HashSet<String>();
873 
874         /*
875          * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation
876          * says to only compare the first certificate in the chain which is
877          * equivalent to the USER_CERTIFICATE prefix for the Android keystore
878          * convention.
879          */
880         final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE, mUid);
881         if (certAliases != null) {
882             for (String alias : certAliases) {
883                 final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
884                 if (certBytes == null) {
885                     continue;
886                 }
887 
888                 nonCaEntries.add(alias);
889 
890                 if (Arrays.equals(certBytes, targetCertBytes)) {
891                     return alias;
892                 }
893             }
894         }
895 
896         /*
897          * Look at all the TrustedCertificateEntry types. Skip all the
898          * PrivateKeyEntry we looked at above.
899          */
900         final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE, mUid);
901         if (certAliases != null) {
902             for (String alias : caAliases) {
903                 if (nonCaEntries.contains(alias)) {
904                     continue;
905                 }
906 
907                 final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
908                 if (certBytes == null) {
909                     continue;
910                 }
911 
912                 if (Arrays.equals(certBytes, targetCertBytes)) {
913                     return alias;
914                 }
915             }
916         }
917 
918         return null;
919     }
920 
921     @Override
engineStore(OutputStream stream, char[] password)922     public void engineStore(OutputStream stream, char[] password) throws IOException,
923             NoSuchAlgorithmException, CertificateException {
924         throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
925     }
926 
927     @Override
engineLoad(InputStream stream, char[] password)928     public void engineLoad(InputStream stream, char[] password) throws IOException,
929             NoSuchAlgorithmException, CertificateException {
930         if (stream != null) {
931             throw new IllegalArgumentException("InputStream not supported");
932         }
933 
934         if (password != null) {
935             throw new IllegalArgumentException("password not supported");
936         }
937 
938         // Unfortunate name collision.
939         mKeyStore = KeyStore.getInstance();
940         mUid = KeyStore.UID_SELF;
941     }
942 
943     @Override
engineLoad(LoadStoreParameter param)944     public void engineLoad(LoadStoreParameter param) throws IOException,
945             NoSuchAlgorithmException, CertificateException {
946         int uid = KeyStore.UID_SELF;
947         if (param != null) {
948             if (param instanceof AndroidKeyStoreLoadStoreParameter) {
949                 uid = ((AndroidKeyStoreLoadStoreParameter) param).getUid();
950             } else {
951                 throw new IllegalArgumentException(
952                         "Unsupported param type: " + param.getClass());
953             }
954         }
955         mKeyStore = KeyStore.getInstance();
956         mUid = uid;
957     }
958 
959     @Override
engineSetEntry(String alias, Entry entry, ProtectionParameter param)960     public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
961             throws KeyStoreException {
962         if (entry == null) {
963             throw new KeyStoreException("entry == null");
964         }
965 
966         Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
967 
968         if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
969             java.security.KeyStore.TrustedCertificateEntry trE =
970                     (java.security.KeyStore.TrustedCertificateEntry) entry;
971             engineSetCertificateEntry(alias, trE.getTrustedCertificate());
972             return;
973         }
974 
975         if (entry instanceof PrivateKeyEntry) {
976             PrivateKeyEntry prE = (PrivateKeyEntry) entry;
977             setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param);
978         } else if (entry instanceof SecretKeyEntry) {
979             SecretKeyEntry secE = (SecretKeyEntry) entry;
980             setSecretKeyEntry(alias, secE.getSecretKey(), param);
981         } else {
982             throw new KeyStoreException(
983                     "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
984                     + "; was " + entry);
985         }
986     }
987 
988     /**
989      * {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from
990      * {@link #getPublicKey()}. This is so that crypto operations on these public keys contain
991      * can find out which keystore private key entry to use. This is needed so that Android Keystore
992      * crypto operations using public keys can find out which key alias to use. These operations
993      * require an alias.
994      */
995     static class KeyStoreX509Certificate extends DelegatingX509Certificate {
996         private final String mPrivateKeyAlias;
997         private final int mPrivateKeyUid;
KeyStoreX509Certificate(String privateKeyAlias, int privateKeyUid, X509Certificate delegate)998         KeyStoreX509Certificate(String privateKeyAlias, int privateKeyUid,
999                 X509Certificate delegate) {
1000             super(delegate);
1001             mPrivateKeyAlias = privateKeyAlias;
1002             mPrivateKeyUid = privateKeyUid;
1003         }
1004 
1005         @Override
getPublicKey()1006         public PublicKey getPublicKey() {
1007             PublicKey original = super.getPublicKey();
1008             return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
1009                     mPrivateKeyAlias, mPrivateKeyUid,
1010                     original.getAlgorithm(), original.getEncoded());
1011         }
1012     }
1013 }
1014