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