• 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.keystore2;
18 
19 import android.annotation.NonNull;
20 import android.hardware.biometrics.BiometricManager;
21 import android.hardware.security.keymint.HardwareAuthenticatorType;
22 import android.hardware.security.keymint.KeyParameter;
23 import android.hardware.security.keymint.SecurityLevel;
24 import android.security.GateKeeper;
25 import android.security.KeyStore2;
26 import android.security.KeyStoreParameter;
27 import android.security.KeyStoreSecurityLevel;
28 import android.security.keymaster.KeymasterDefs;
29 import android.security.keystore.KeyGenParameterSpec;
30 import android.security.keystore.KeyPermanentlyInvalidatedException;
31 import android.security.keystore.KeyProperties;
32 import android.security.keystore.KeyProtection;
33 import android.security.keystore.SecureKeyImportUnavailableException;
34 import android.security.keystore.WrappedKeyEntry;
35 import android.system.keystore2.AuthenticatorSpec;
36 import android.system.keystore2.Domain;
37 import android.system.keystore2.IKeystoreSecurityLevel;
38 import android.system.keystore2.KeyDescriptor;
39 import android.system.keystore2.KeyEntryResponse;
40 import android.system.keystore2.KeyMetadata;
41 import android.system.keystore2.ResponseCode;
42 import android.util.Log;
43 
44 import com.android.internal.annotations.VisibleForTesting;
45 
46 import java.io.ByteArrayInputStream;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.io.OutputStream;
50 import java.security.Key;
51 import java.security.KeyStore.Entry;
52 import java.security.KeyStore.LoadStoreParameter;
53 import java.security.KeyStore.PrivateKeyEntry;
54 import java.security.KeyStore.ProtectionParameter;
55 import java.security.KeyStore.SecretKeyEntry;
56 import java.security.KeyStoreException;
57 import java.security.KeyStoreSpi;
58 import java.security.NoSuchAlgorithmException;
59 import java.security.PrivateKey;
60 import java.security.ProviderException;
61 import java.security.UnrecoverableKeyException;
62 import java.security.cert.Certificate;
63 import java.security.cert.CertificateEncodingException;
64 import java.security.cert.CertificateException;
65 import java.security.cert.CertificateFactory;
66 import java.security.cert.X509Certificate;
67 import java.util.ArrayList;
68 import java.util.Arrays;
69 import java.util.Collection;
70 import java.util.Collections;
71 import java.util.Date;
72 import java.util.Enumeration;
73 import java.util.HashSet;
74 import java.util.Iterator;
75 import java.util.List;
76 import java.util.Set;
77 
78 import javax.crypto.SecretKey;
79 
80 /**
81  * A java.security.KeyStore interface for the Android KeyStore. An instance of
82  * it can be created via the {@link java.security.KeyStore#getInstance(String)
83  * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a
84  * java.security.KeyStore backed by this "AndroidKeyStore" implementation.
85  * <p>
86  * This is built on top of Android's keystore daemon. The convention of alias
87  * use is:
88  * <p>
89  * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key,
90  * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one
91  * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE
92  * entry which will have the rest of the chain concatenated in BER format.
93  * <p>
94  * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry
95  * with a single certificate.
96  *
97  * @hide
98  */
99 public class AndroidKeyStoreSpi extends KeyStoreSpi {
100     public static final String TAG = "AndroidKeyStoreSpi";
101     public static final String NAME = "AndroidKeyStore";
102 
103     private KeyStore2 mKeyStore;
104     private @KeyProperties.Namespace int mNamespace = KeyProperties.NAMESPACE_APPLICATION;
105 
106     @Override
engineGetKey(String alias, char[] password)107     public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
108             UnrecoverableKeyException {
109         try {
110             return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(mKeyStore,
111                                                                               alias,
112                                                                               mNamespace);
113         } catch (KeyPermanentlyInvalidatedException e) {
114             throw new UnrecoverableKeyException(e.getMessage());
115         } catch (UnrecoverableKeyException e) {
116             Throwable cause = e.getCause();
117             if (cause instanceof android.security.KeyStoreException) {
118                 if (((android.security.KeyStoreException) cause).getErrorCode()
119                         == ResponseCode.KEY_NOT_FOUND) {
120                     return null;
121                 }
122             }
123             throw e;
124         }
125     }
126 
127     /**
128      * Make a key descriptor from the given alias and the mNamespace member.
129      * If mNamespace is -1 it sets the domain field to {@link Domain#APP} and {@link Domain#SELINUX}
130      * otherwise. The blob field is always set to null and the alias field to {@code alias}
131      * @param alias The alias of the new key descriptor.
132      * @return A new key descriptor.
133      */
makeKeyDescriptor(@onNull String alias)134     private KeyDescriptor makeKeyDescriptor(@NonNull String alias) {
135         KeyDescriptor descriptor = new KeyDescriptor();
136         descriptor.domain = getTargetDomain();
137         descriptor.nspace = mNamespace; // ignored if Domain.App;
138         descriptor.alias = alias;
139         descriptor.blob = null;
140         return descriptor;
141     }
142 
getTargetDomain()143     private @Domain int getTargetDomain() {
144         return mNamespace == KeyProperties.NAMESPACE_APPLICATION
145                 ? Domain.APP
146                 : Domain.SELINUX;
147     }
getKeyMetadata(String alias)148     private KeyEntryResponse getKeyMetadata(String alias) {
149         if (alias == null) {
150             throw new NullPointerException("alias == null");
151         }
152 
153         KeyDescriptor descriptor = makeKeyDescriptor(alias);
154 
155         try {
156             return mKeyStore.getKeyEntry(descriptor);
157         } catch (android.security.KeyStoreException e) {
158             if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
159                 Log.w(TAG, "Could not get key metadata from Keystore.", e);
160             }
161             return null;
162         }
163     }
164 
165     @Override
engineGetCertificateChain(String alias)166     public Certificate[] engineGetCertificateChain(String alias) {
167         KeyEntryResponse response = getKeyMetadata(alias);
168 
169         if (response == null || response.metadata.certificate == null) {
170             return null;
171         }
172 
173         final X509Certificate leaf = (X509Certificate) toCertificate(response.metadata.certificate);
174         if (leaf == null) {
175             return null;
176         }
177 
178         final Certificate[] caList;
179 
180         final byte[] caBytes = response.metadata.certificateChain;
181 
182         if (caBytes != null) {
183             final Collection<X509Certificate> caChain = toCertificates(caBytes);
184 
185             caList = new Certificate[caChain.size() + 1];
186 
187             final Iterator<X509Certificate> it = caChain.iterator();
188             int i = 1;
189             while (it.hasNext()) {
190                 caList[i++] = it.next();
191             }
192         } else {
193             caList = new Certificate[1];
194         }
195 
196         caList[0] = leaf;
197 
198         return caList;
199     }
200 
201     @Override
engineGetCertificate(String alias)202     public Certificate engineGetCertificate(String alias) {
203         KeyEntryResponse response = getKeyMetadata(alias);
204 
205         if (response == null) {
206             return null;
207         }
208 
209         byte[] encodedCert = response.metadata.certificate;
210         if (encodedCert != null) {
211             return toCertificate(encodedCert);
212         }
213 
214         encodedCert = response.metadata.certificateChain;
215         if (encodedCert != null) {
216             return toCertificate(encodedCert);
217         }
218 
219         // This entry/alias does not contain a certificate.
220         return null;
221     }
222 
toCertificate(byte[] bytes)223     static X509Certificate toCertificate(byte[] bytes) {
224         try {
225             final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
226             return (X509Certificate) certFactory.generateCertificate(
227                     new ByteArrayInputStream(bytes));
228         } catch (CertificateException e) {
229             Log.w(NAME, "Couldn't parse certificate in keystore", e);
230             return null;
231         }
232     }
233 
234     @SuppressWarnings("unchecked")
toCertificates(byte[] bytes)235     private static Collection<X509Certificate> toCertificates(byte[] bytes) {
236         try {
237             final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
238             return (Collection<X509Certificate>) certFactory.generateCertificates(
239                             new ByteArrayInputStream(bytes));
240         } catch (CertificateException e) {
241             Log.w(NAME, "Couldn't parse certificates in keystore", e);
242             return new ArrayList<X509Certificate>();
243         }
244     }
245 
246     @Override
engineGetCreationDate(String alias)247     public Date engineGetCreationDate(String alias) {
248         KeyEntryResponse response = getKeyMetadata(alias);
249 
250         if (response == null) {
251             return null;
252         }
253 
254         if (response.metadata.modificationTimeMs == -1) {
255             return null;
256         }
257         return new Date(response.metadata.modificationTimeMs);
258     }
259 
260     @Override
engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)261     public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
262             throws KeyStoreException {
263         if ((password != null) && (password.length > 0)) {
264             throw new KeyStoreException("entries cannot be protected with passwords");
265         }
266 
267         if (key instanceof PrivateKey) {
268             setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
269         } else if (key instanceof SecretKey) {
270             setSecretKeyEntry(alias, (SecretKey) key, null);
271         } else {
272             throw new KeyStoreException("Only PrivateKey and SecretKey are supported");
273         }
274     }
275 
getLegacyKeyProtectionParameter(PrivateKey key)276     private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key)
277             throws KeyStoreException {
278         String keyAlgorithm = key.getAlgorithm();
279         KeyProtection.Builder specBuilder;
280         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
281             specBuilder =
282                     new KeyProtection.Builder(
283                             KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
284             // Authorized to be used with any digest (including no digest).
285             // MD5 was never offered for Android Keystore for ECDSA.
286             specBuilder.setDigests(
287                     KeyProperties.DIGEST_NONE,
288                     KeyProperties.DIGEST_SHA1,
289                     KeyProperties.DIGEST_SHA224,
290                     KeyProperties.DIGEST_SHA256,
291                     KeyProperties.DIGEST_SHA384,
292                     KeyProperties.DIGEST_SHA512);
293         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
294             specBuilder =
295                     new KeyProtection.Builder(
296                             KeyProperties.PURPOSE_ENCRYPT
297                             | KeyProperties.PURPOSE_DECRYPT
298                             | KeyProperties.PURPOSE_SIGN
299                             | KeyProperties.PURPOSE_VERIFY);
300             // Authorized to be used with any digest (including no digest).
301             specBuilder.setDigests(
302                     KeyProperties.DIGEST_NONE,
303                     KeyProperties.DIGEST_MD5,
304                     KeyProperties.DIGEST_SHA1,
305                     KeyProperties.DIGEST_SHA224,
306                     KeyProperties.DIGEST_SHA256,
307                     KeyProperties.DIGEST_SHA384,
308                     KeyProperties.DIGEST_SHA512);
309             // Authorized to be used with any encryption and signature padding
310             // schemes (including no padding).
311             specBuilder.setEncryptionPaddings(
312                     KeyProperties.ENCRYPTION_PADDING_NONE,
313                     KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
314                     KeyProperties.ENCRYPTION_PADDING_RSA_OAEP);
315             specBuilder.setSignaturePaddings(
316                     KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
317                     KeyProperties.SIGNATURE_PADDING_RSA_PSS);
318             // Disable randomized encryption requirement to support encryption
319             // padding NONE above.
320             specBuilder.setRandomizedEncryptionRequired(false);
321         } else {
322             throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
323         }
324         specBuilder.setUserAuthenticationRequired(false);
325 
326         return specBuilder.build();
327     }
328 
setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain, java.security.KeyStore.ProtectionParameter param)329     private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
330             java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
331         @SecurityLevel int securitylevel = SecurityLevel.TRUSTED_ENVIRONMENT;
332         int flags = 0;
333         KeyProtection spec;
334         if (param == null) {
335             spec = getLegacyKeyProtectionParameter(key);
336         } else if (param instanceof KeyStoreParameter) {
337             spec = getLegacyKeyProtectionParameter(key);
338             KeyStoreParameter legacySpec = (KeyStoreParameter) param;
339         } else if (param instanceof KeyProtection) {
340             spec = (KeyProtection) param;
341             if (spec.isCriticalToDeviceEncryption()) {
342                 // This key is should not be bound to the LSKF even if it is auth bound.
343                 // This indicates that this key is used in the derivation for of the
344                 // master key, that is used for the LSKF binding of other auth bound
345                 // keys. This breaks up a circular dependency while retaining logical
346                 // authentication binding of the key.
347                 flags |= IKeystoreSecurityLevel
348                         .KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING;
349             }
350 
351             if (spec.isStrongBoxBacked()) {
352                 securitylevel = SecurityLevel.STRONGBOX;
353             }
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         @Domain int targetDomain = getTargetDomain();
428 
429         // If the given key is an AndroidKeyStorePrivateKey, we attempt to update
430         // its subcomponents with the given certificate and certificate chain.
431         if (key instanceof AndroidKeyStorePrivateKey) {
432             AndroidKeyStoreKey ksKey = (AndroidKeyStoreKey) key;
433             KeyDescriptor descriptor = ksKey.getUserKeyDescriptor();
434 
435             // This throws if the request cannot replace the entry.
436             assertCanReplace(alias, targetDomain, mNamespace, descriptor);
437 
438             try {
439                 mKeyStore.updateSubcomponents(
440                         ((AndroidKeyStorePrivateKey) key).getKeyIdDescriptor(),
441                         userCertBytes, chainBytes);
442             } catch (android.security.KeyStoreException e) {
443                 throw new KeyStoreException("Failed to store certificate and certificate chain", e);
444             }
445             return;
446         }
447 
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         byte[] pkcs8EncodedPrivateKeyBytes = key.getEncoded();
459         if (pkcs8EncodedPrivateKeyBytes == null) {
460             throw new KeyStoreException("Private key did not export any key material");
461         }
462 
463         final List<KeyParameter> importArgs = new ArrayList<>();
464 
465         try {
466             importArgs.add(KeyStore2ParameterUtils.makeEnum(
467                     KeymasterDefs.KM_TAG_ALGORITHM,
468                     KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
469                             key.getAlgorithm()))
470             );
471             KeyStore2ParameterUtils.forEachSetFlag(spec.getPurposes(), (purpose) -> {
472                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
473                         KeymasterDefs.KM_TAG_PURPOSE,
474                         KeyProperties.Purpose.toKeymaster(purpose)
475                 ));
476             });
477             if (spec.isDigestsSpecified()) {
478                 for (String digest : spec.getDigests()) {
479                     importArgs.add(KeyStore2ParameterUtils.makeEnum(
480                             KeymasterDefs.KM_TAG_DIGEST,
481                             KeyProperties.Digest.toKeymaster(digest)
482                     ));
483                 }
484             }
485             for (String blockMode : spec.getBlockModes()) {
486                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
487                         KeymasterDefs.KM_TAG_BLOCK_MODE,
488                         KeyProperties.BlockMode.toKeymaster(blockMode)
489                 ));
490             }
491             int[] keymasterEncryptionPaddings =
492                     KeyProperties.EncryptionPadding.allToKeymaster(
493                             spec.getEncryptionPaddings());
494             if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
495                     && (spec.isRandomizedEncryptionRequired())) {
496                 for (int keymasterPadding : keymasterEncryptionPaddings) {
497                     if (!KeymasterUtils
498                             .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
499                                     keymasterPadding)) {
500                         throw new KeyStoreException(
501                                 "Randomized encryption (IND-CPA) required but is violated by"
502                                         + " encryption padding mode: "
503                                         + KeyProperties.EncryptionPadding.fromKeymaster(
504                                         keymasterPadding)
505                                         + ". See KeyProtection documentation.");
506                     }
507                 }
508             }
509             for (int padding : keymasterEncryptionPaddings) {
510                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
511                         KeymasterDefs.KM_TAG_PADDING,
512                         padding
513                 ));
514             }
515             for (String padding : spec.getSignaturePaddings()) {
516                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
517                         KeymasterDefs.KM_TAG_PADDING,
518                         KeyProperties.SignaturePadding.toKeymaster(padding)
519                 ));
520             }
521             KeyStore2ParameterUtils.addUserAuthArgs(importArgs, spec);
522             if (spec.getKeyValidityStart() != null) {
523                 importArgs.add(KeyStore2ParameterUtils.makeDate(
524                         KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()
525                 ));
526             }
527             if (spec.getKeyValidityForOriginationEnd() != null) {
528                 importArgs.add(KeyStore2ParameterUtils.makeDate(
529                         KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
530                         spec.getKeyValidityForOriginationEnd()
531                 ));
532             }
533             if (spec.getKeyValidityForConsumptionEnd() != null) {
534                 importArgs.add(KeyStore2ParameterUtils.makeDate(
535                         KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
536                         spec.getKeyValidityForConsumptionEnd()
537                 ));
538             }
539             if (spec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
540                 importArgs.add(KeyStore2ParameterUtils.makeInt(
541                         KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
542                         spec.getMaxUsageCount()
543                 ));
544             }
545         } catch (IllegalArgumentException | IllegalStateException e) {
546             throw new KeyStoreException(e);
547         }
548 
549         try {
550             KeyStoreSecurityLevel securityLevelInterface = mKeyStore.getSecurityLevel(
551                     securitylevel);
552 
553             KeyDescriptor descriptor = makeKeyDescriptor(alias);
554 
555             KeyMetadata metadata = securityLevelInterface.importKey(descriptor, null,
556                     importArgs, flags, pkcs8EncodedPrivateKeyBytes);
557 
558             try {
559                 mKeyStore.updateSubcomponents(metadata.key, userCertBytes, chainBytes);
560             } catch (android.security.KeyStoreException e) {
561                 mKeyStore.deleteKey(metadata.key);
562                 throw new KeyStoreException("Failed to store certificate and certificate chain", e);
563             }
564 
565         } catch (android.security.KeyStoreException e) {
566             throw new KeyStoreException("Failed to store private key", e);
567         }
568     }
569 
assertCanReplace(String alias, @Domain int targetDomain, int targetNamespace, KeyDescriptor descriptor)570     private static void assertCanReplace(String alias, @Domain int targetDomain,
571             int targetNamespace, KeyDescriptor descriptor)
572             throws KeyStoreException {
573         // If
574         //  * the alias does not match, or
575         //  * the domain does not match, or
576         //  * the domain is Domain.SELINUX and the namespaces don not match,
577         // then the designated key location is not equivalent to the location of the
578         // given key parameter and cannot be updated.
579         //
580         // Note: mNamespace == KeyProperties.NAMESPACE_APPLICATION implies that the target domain
581         // is Domain.APP and Domain.SELINUX is the target domain otherwise.
582         if (alias != descriptor.alias
583                 || descriptor.domain != targetDomain
584                 || (descriptor.domain == Domain.SELINUX && descriptor.nspace != targetNamespace)) {
585             throw new KeyStoreException("Can only replace keys with same alias: " + alias
586                     + " != " + descriptor.alias + " in the same target domain: " + targetDomain
587                     + " != " + descriptor.domain
588                     + (targetDomain == Domain.SELINUX ? " in the same target namespace: "
589                     + targetNamespace + " != " + descriptor.nspace : "")
590             );
591         }
592     }
593 
setSecretKeyEntry(String alias, SecretKey key, java.security.KeyStore.ProtectionParameter param)594     private void setSecretKeyEntry(String alias, SecretKey key,
595             java.security.KeyStore.ProtectionParameter param)
596             throws KeyStoreException {
597         if ((param != null) && (!(param instanceof KeyProtection))) {
598             throw new KeyStoreException(
599                     "Unsupported protection parameter class: " + param.getClass().getName()
600                     + ". Supported: " + KeyProtection.class.getName());
601         }
602         KeyProtection params = (KeyProtection) param;
603 
604         @SecurityLevel int securityLevel = params.isStrongBoxBacked() ? SecurityLevel.STRONGBOX :
605                 SecurityLevel.TRUSTED_ENVIRONMENT;
606         @Domain int targetDomain = (getTargetDomain());
607 
608         if (key instanceof AndroidKeyStoreSecretKey) {
609             String keyAliasInKeystore =
610                     ((AndroidKeyStoreSecretKey) key).getUserKeyDescriptor().alias;
611 
612             KeyDescriptor descriptor = ((AndroidKeyStoreSecretKey) key).getUserKeyDescriptor();
613 
614             // This throws if the request cannot replace the existing key.
615             assertCanReplace(alias, targetDomain, mNamespace, descriptor);
616 
617             // This is the entry where this key is already stored. No need to do anything.
618             if (params != null) {
619                 throw new KeyStoreException("Modifying KeyStore-backed key using protection"
620                         + " parameters not supported");
621             }
622             return;
623         }
624 
625         if (params == null) {
626             throw new KeyStoreException(
627                     "Protection parameters must be specified when importing a symmetric key");
628         }
629 
630         // Not a KeyStore-backed secret key -- import its key material into keystore.
631         String keyExportFormat = key.getFormat();
632         if (keyExportFormat == null) {
633             throw new KeyStoreException(
634                     "Only secret keys that export their key material are supported");
635         } else if (!"RAW".equals(keyExportFormat)) {
636             throw new KeyStoreException(
637                     "Unsupported secret key material export format: " + keyExportFormat);
638         }
639         byte[] keyMaterial = key.getEncoded();
640         if (keyMaterial == null) {
641             throw new KeyStoreException("Key did not export its key material despite supporting"
642                     + " RAW format export");
643         }
644 
645         final List<KeyParameter> importArgs = new ArrayList<>();
646 
647         try {
648             int keymasterAlgorithm =
649                     KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(
650                             key.getAlgorithm());
651 
652             importArgs.add(KeyStore2ParameterUtils.makeEnum(
653                     KeymasterDefs.KM_TAG_ALGORITHM,
654                     keymasterAlgorithm
655             ));
656 
657             if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
658                 // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
659                 // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one
660                 // digest, we don't let import parameters override the digest implied by the key.
661                 // If the parameters specify digests at all, they must specify only one digest, the
662                 // only implied by key algorithm.
663                 int keymasterImpliedDigest =
664                         KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm());
665                 if (keymasterImpliedDigest == -1) {
666                     throw new ProviderException(
667                             "HMAC key algorithm digest unknown for key algorithm "
668                                     + key.getAlgorithm());
669                 }
670 
671                 if (params.isDigestsSpecified()) {
672                     // Digest(s) explicitly specified in params -- check that the list consists of
673                     // exactly one digest, the one implied by key algorithm.
674                     int[] keymasterDigestsFromParams =
675                             KeyProperties.Digest.allToKeymaster(params.getDigests());
676                     if ((keymasterDigestsFromParams.length != 1)
677                             || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) {
678                         throw new KeyStoreException(
679                                 "Unsupported digests specification: "
680                                         + Arrays.asList(params.getDigests()) + ". Only "
681                                         + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest)
682                                         + " supported for HMAC key algorithm "
683                                         + key.getAlgorithm());
684                     }
685                 }
686                 int outputBits = KeymasterUtils.getDigestOutputSizeBits(keymasterImpliedDigest);
687                 if (outputBits == -1) {
688                     throw new ProviderException(
689                             "HMAC key authorized for unsupported digest: "
690                                     + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest));
691                 }
692                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
693                         KeymasterDefs.KM_TAG_DIGEST, keymasterImpliedDigest
694                 ));
695                 importArgs.add(KeyStore2ParameterUtils.makeInt(
696                         KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, outputBits
697                 ));
698             } else {
699                 if (params.isDigestsSpecified()) {
700                     for (String digest : params.getDigests()) {
701                         importArgs.add(KeyStore2ParameterUtils.makeEnum(
702                                 KeymasterDefs.KM_TAG_DIGEST,
703                                 KeyProperties.Digest.toKeymaster(digest)
704                         ));
705                     }
706                 }
707             }
708 
709             KeyStore2ParameterUtils.forEachSetFlag(params.getPurposes(), (purpose) -> {
710                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
711                         KeymasterDefs.KM_TAG_PURPOSE,
712                         KeyProperties.Purpose.toKeymaster(purpose)
713                 ));
714             });
715 
716             boolean indCpa = false;
717             if ((params.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0) {
718                 if (((KeyProtection) param).isRandomizedEncryptionRequired()) {
719                     indCpa = true;
720                 } else {
721                     importArgs.add(KeyStore2ParameterUtils.makeBool(
722                             KeymasterDefs.KM_TAG_CALLER_NONCE
723                     ));
724                 }
725             }
726 
727             for (String blockMode : params.getBlockModes()) {
728                 int keymasterBlockMode = KeyProperties.BlockMode.toKeymaster(blockMode);
729                 if (indCpa
730                         && !KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
731                                 keymasterBlockMode)) {
732                     throw new KeyStoreException(
733                             "Randomized encryption (IND-CPA) required but may be violated by"
734                                     + " block mode: " + blockMode
735                                     + ". See KeyProtection documentation.");
736 
737                 }
738                 if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES
739                         && keymasterBlockMode == KeymasterDefs.KM_MODE_GCM) {
740                     importArgs.add(KeyStore2ParameterUtils.makeInt(
741                             KeymasterDefs.KM_TAG_MIN_MAC_LENGTH,
742                             AndroidKeyStoreAuthenticatedAESCipherSpi.GCM
743                                     .MIN_SUPPORTED_TAG_LENGTH_BITS
744                     ));
745                 }
746                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
747                         KeymasterDefs.KM_TAG_BLOCK_MODE,
748                         keymasterBlockMode
749                 ));
750             }
751 
752             if (params.getSignaturePaddings().length > 0) {
753                 throw new KeyStoreException("Signature paddings not supported for symmetric keys");
754             }
755 
756             for (String padding : params.getEncryptionPaddings()) {
757                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
758                         KeymasterDefs.KM_TAG_PADDING,
759                         KeyProperties.EncryptionPadding.toKeymaster(padding)
760                 ));
761             }
762 
763             KeyStore2ParameterUtils.addUserAuthArgs(importArgs, params);
764 
765             if (params.getKeyValidityStart() != null) {
766                 importArgs.add(KeyStore2ParameterUtils.makeDate(
767                         KeymasterDefs.KM_TAG_ACTIVE_DATETIME, params.getKeyValidityStart()
768                 ));
769             }
770             if (params.getKeyValidityForOriginationEnd() != null) {
771                 importArgs.add(KeyStore2ParameterUtils.makeDate(
772                         KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
773                         params.getKeyValidityForOriginationEnd()
774                 ));
775             }
776             if (params.getKeyValidityForConsumptionEnd() != null) {
777                 importArgs.add(KeyStore2ParameterUtils.makeDate(
778                         KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
779                         params.getKeyValidityForConsumptionEnd()
780                 ));
781             }
782             if (params.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
783                 importArgs.add(KeyStore2ParameterUtils.makeInt(
784                         KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
785                         params.getMaxUsageCount()
786                 ));
787             }
788         } catch (IllegalArgumentException | IllegalStateException e) {
789             throw new KeyStoreException(e);
790         }
791 
792         int flags = 0;
793         if (params.isCriticalToDeviceEncryption()) {
794             flags |= IKeystoreSecurityLevel.KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING;
795         }
796 
797         try {
798             KeyStoreSecurityLevel securityLevelInterface = mKeyStore.getSecurityLevel(
799                     securityLevel);
800 
801             KeyDescriptor descriptor = makeKeyDescriptor(alias);
802 
803             securityLevelInterface.importKey(descriptor, null /* TODO attestationKey */,
804                     importArgs, flags, keyMaterial);
805         } catch (android.security.KeyStoreException e) {
806             throw new KeyStoreException("Failed to import secret key.", e);
807         }
808     }
809 
setWrappedKeyEntry(String alias, WrappedKeyEntry entry, java.security.KeyStore.ProtectionParameter param)810     private void setWrappedKeyEntry(String alias, WrappedKeyEntry entry,
811             java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
812         if (param != null) {
813             throw new KeyStoreException("Protection parameters are specified inside wrapped keys");
814         }
815 
816         byte[] maskingKey = new byte[32];
817 
818         String[] parts = entry.getTransformation().split("/");
819 
820         List<KeyParameter> args = new ArrayList<>();
821 
822         String algorithm = parts[0];
823         if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
824             args.add(KeyStore2ParameterUtils.makeEnum(
825                     KeymasterDefs.KM_TAG_ALGORITHM,
826                     KeymasterDefs.KM_ALGORITHM_RSA
827             ));
828         } else {
829             throw new KeyStoreException("Algorithm \"" + algorithm + "\" not supported for "
830                     + "wrapping. Only RSA wrapping keys are supported.");
831         }
832 
833         if (parts.length > 1) {
834             String mode = parts[1];
835             args.add(KeyStore2ParameterUtils.makeEnum(
836                     KeymasterDefs.KM_TAG_BLOCK_MODE,
837                     KeyProperties.BlockMode.toKeymaster(mode)
838             ));
839         }
840 
841         if (parts.length > 2) {
842             @KeyProperties.EncryptionPaddingEnum int padding =
843                     KeyProperties.EncryptionPadding.toKeymaster(parts[2]);
844             if (padding != KeymasterDefs.KM_PAD_NONE) {
845                 args.add(KeyStore2ParameterUtils.makeEnum(
846                         KeymasterDefs.KM_TAG_PADDING,
847                         padding
848                 ));
849             }
850         }
851 
852         KeyGenParameterSpec spec = (KeyGenParameterSpec) entry.getAlgorithmParameterSpec();
853         if (spec.isDigestsSpecified()) {
854             @KeyProperties.DigestEnum int digest =
855                     KeyProperties.Digest.toKeymaster(spec.getDigests()[0]);
856             if (digest != KeymasterDefs.KM_DIGEST_NONE) {
857                 args.add(KeyStore2ParameterUtils.makeEnum(
858                         KeymasterDefs.KM_TAG_DIGEST,
859                         digest
860                 ));
861             }
862         }
863 
864         KeyDescriptor wrappingkey = makeKeyDescriptor(entry.getWrappingKeyAlias());
865 
866         KeyEntryResponse response = null;
867         try {
868             response = mKeyStore.getKeyEntry(wrappingkey);
869         } catch (android.security.KeyStoreException e) {
870             throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
871                     + e.getErrorCode(), e);
872         }
873 
874         KeyDescriptor wrappedKey = makeKeyDescriptor(alias);
875 
876         KeyStoreSecurityLevel securityLevel = new KeyStoreSecurityLevel(response.iSecurityLevel);
877 
878         final BiometricManager bm = android.app.AppGlobals.getInitialApplication()
879                 .getSystemService(BiometricManager.class);
880 
881         long[] biometricSids = bm.getAuthenticatorIds();
882 
883         List<AuthenticatorSpec> authenticatorSpecs = new ArrayList<>();
884 
885         AuthenticatorSpec authenticatorSpec = new AuthenticatorSpec();
886         authenticatorSpec.authenticatorType = HardwareAuthenticatorType.PASSWORD;
887         authenticatorSpec.authenticatorId = GateKeeper.getSecureUserId();
888         authenticatorSpecs.add(authenticatorSpec);
889 
890         for (long sid : biometricSids) {
891             AuthenticatorSpec authSpec = new AuthenticatorSpec();
892             authSpec.authenticatorType = HardwareAuthenticatorType.FINGERPRINT;
893             authSpec.authenticatorId = sid;
894             authenticatorSpecs.add(authSpec);
895         }
896 
897         try {
898             securityLevel.importWrappedKey(
899                     wrappedKey, wrappingkey,
900                     entry.getWrappedKeyBytes(),
901                     null /* masking key is set to 32 bytes if null is given here */,
902                     args,
903                     authenticatorSpecs.toArray(new AuthenticatorSpec[0]));
904         } catch (android.security.KeyStoreException e) {
905             switch (e.getErrorCode()) {
906                 case KeymasterDefs.KM_ERROR_UNIMPLEMENTED: {
907                     throw new SecureKeyImportUnavailableException("Could not import wrapped key");
908                 }
909                 default:
910                     throw new KeyStoreException("Failed to import wrapped key. Keystore error "
911                             + "code: " + e.getErrorCode(), e);
912             }
913         }
914     }
915 
916     @Override
engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)917     public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
918             throws KeyStoreException {
919         throw new KeyStoreException("Operation not supported because key encoding is unknown");
920     }
921 
922     /**
923      * This function sets a trusted certificate entry. It fails if the given
924      * alias is already taken by an actual key entry. However, if the entry is a
925      * trusted certificate it will get silently replaced.
926      * @param alias the alias name
927      * @param cert the certificate
928      *
929      * @throws KeyStoreException if the alias is already taken by a secret or private
930      *         key entry.
931      * @throws KeyStoreException with a nested {@link CertificateEncodingException}
932      *         if the {@code cert.getEncoded()} throws.
933      * @throws KeyStoreException with a nested {@link android.security.KeyStoreException} if
934      *         something went wrong while inserting the certificate into keystore.
935      * @throws NullPointerException if cert or alias is null.
936      *
937      * @hide
938      */
939     @Override
engineSetCertificateEntry(String alias, Certificate cert)940     public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
941         if (isKeyEntry(alias)) {
942             throw new KeyStoreException("Entry exists and is not a trusted certificate");
943         }
944 
945         // We can't set something to null.
946         if (cert == null) {
947             throw new NullPointerException("cert == null");
948         }
949 
950         final byte[] encoded;
951         try {
952             encoded = cert.getEncoded();
953         } catch (CertificateEncodingException e) {
954             throw new KeyStoreException(e);
955         }
956 
957         try {
958             mKeyStore.updateSubcomponents(makeKeyDescriptor(alias),
959                     null /* publicCert - unused when used as pure certificate store. */,
960                     encoded);
961         } catch (android.security.KeyStoreException e) {
962             throw new KeyStoreException("Couldn't insert certificate.", e);
963         }
964     }
965 
966     @Override
engineDeleteEntry(String alias)967     public void engineDeleteEntry(String alias) throws KeyStoreException {
968         KeyDescriptor descriptor = makeKeyDescriptor(alias);
969         try {
970             mKeyStore.deleteKey(descriptor);
971         } catch (android.security.KeyStoreException e) {
972             if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
973                 throw new KeyStoreException("Failed to delete entry: " + alias, e);
974             }
975         }
976     }
977 
getUniqueAliases()978     private Set<String> getUniqueAliases() {
979         try {
980             final KeyDescriptor[] keys = mKeyStore.list(
981                     getTargetDomain(),
982                     mNamespace
983             );
984             final Set<String> aliases = new HashSet<>(keys.length);
985             for (KeyDescriptor d : keys) {
986                 aliases.add(d.alias);
987             }
988             return aliases;
989         } catch (android.security.KeyStoreException e) {
990             Log.e(TAG, "Failed to list keystore entries.", e);
991             return new HashSet<>();
992         }
993     }
994 
995     @Override
engineAliases()996     public Enumeration<String> engineAliases() {
997         return Collections.enumeration(getUniqueAliases());
998     }
999 
1000     @Override
engineContainsAlias(String alias)1001     public boolean engineContainsAlias(String alias) {
1002         if (alias == null) {
1003             throw new NullPointerException("alias == null");
1004         }
1005 
1006         return getKeyMetadata(alias) != null;
1007     }
1008 
1009     @Override
engineSize()1010     public int engineSize() {
1011         return getUniqueAliases().size();
1012     }
1013 
1014     @Override
engineIsKeyEntry(String alias)1015     public boolean engineIsKeyEntry(String alias) {
1016         return isKeyEntry(alias);
1017     }
1018 
isKeyEntry(String alias)1019     private boolean isKeyEntry(String alias) {
1020         if (alias == null) {
1021             throw new NullPointerException("alias == null");
1022         }
1023 
1024         KeyEntryResponse response = getKeyMetadata(alias);
1025         // If response is null, there is no such entry.
1026         // If response.iSecurityLevel is null, there is no private or secret key material stored.
1027         return response != null && response.iSecurityLevel != null;
1028     }
1029 
1030 
1031     @Override
engineIsCertificateEntry(String alias)1032     public boolean engineIsCertificateEntry(String alias) {
1033         if (alias == null) {
1034             throw new NullPointerException("alias == null");
1035         }
1036         KeyEntryResponse response = getKeyMetadata(alias);
1037         // If response == null there is no such entry.
1038         // If there is no certificateChain, then this is not a certificate entry.
1039         // If there is a private key entry, this is the certificate chain for that
1040         // key entry and not a CA certificate entry.
1041         return response != null
1042                 && response.metadata.certificateChain != null
1043                 && response.iSecurityLevel == null;
1044     }
1045 
1046     @Override
engineGetCertificateAlias(Certificate cert)1047     public String engineGetCertificateAlias(Certificate cert) {
1048         if (cert == null) {
1049             return null;
1050         }
1051         if (!"X.509".equalsIgnoreCase(cert.getType())) {
1052             Log.e(TAG, "In engineGetCertificateAlias: only X.509 certificates are supported.");
1053             return null;
1054         }
1055         byte[] targetCertBytes;
1056         try {
1057             targetCertBytes = cert.getEncoded();
1058         } catch (CertificateEncodingException e) {
1059             Log.e(TAG, "While trying to get the alias for a certificate.", e);
1060             return null;
1061         }
1062         if (targetCertBytes == null) {
1063             return null;
1064         }
1065 
1066         KeyDescriptor[] keyDescriptors = null;
1067         try {
1068             keyDescriptors = mKeyStore.list(
1069                     getTargetDomain(),
1070                     mNamespace
1071             );
1072         } catch (android.security.KeyStoreException e) {
1073             Log.w(TAG, "Failed to get list of keystore entries.", e);
1074         }
1075 
1076         String caAlias = null;
1077         for (KeyDescriptor d : keyDescriptors) {
1078             KeyEntryResponse response = getKeyMetadata(d.alias);
1079             if (response == null) {
1080                 continue;
1081             }
1082             /*
1083              * The KeyStoreSpi documentation says to only compare the first certificate in the
1084              * chain which is equivalent to the {@code response.metadata.certificate} field.
1085              * So we look for a hit in this field first. For pure CA certificate entries,
1086              * we check the {@code response.metadata.certificateChain} field. But we only
1087              * return a CA alias if there was no hit in the certificate field of any other
1088              * entry.
1089              */
1090             if (response.metadata.certificate != null) {
1091                 if (Arrays.equals(response.metadata.certificate, targetCertBytes)) {
1092                     return d.alias;
1093                 }
1094             } else if (response.metadata.certificateChain != null && caAlias == null) {
1095                 if (Arrays.equals(response.metadata.certificateChain, targetCertBytes)) {
1096                     caAlias =  d.alias;
1097                 }
1098             }
1099         }
1100         return caAlias;
1101     }
1102 
1103     /**
1104      * Used by Tests to initialize with a fake KeyStore2.
1105      * @hide
1106      * @param keystore
1107      */
1108     @VisibleForTesting
initForTesting(KeyStore2 keystore)1109     public void initForTesting(KeyStore2 keystore) {
1110         mKeyStore = keystore;
1111         mNamespace = KeyProperties.NAMESPACE_APPLICATION;
1112     }
1113 
1114     @Override
engineStore(OutputStream stream, char[] password)1115     public void engineStore(OutputStream stream, char[] password) throws IOException,
1116             NoSuchAlgorithmException, CertificateException {
1117         throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
1118     }
1119 
1120     @Override
engineLoad(InputStream stream, char[] password)1121     public void engineLoad(InputStream stream, char[] password) throws IOException,
1122             NoSuchAlgorithmException, CertificateException {
1123         if (stream != null) {
1124             throw new IllegalArgumentException("InputStream not supported");
1125         }
1126 
1127         if (password != null) {
1128             throw new IllegalArgumentException("password not supported");
1129         }
1130 
1131         // Unfortunate name collision.
1132         mKeyStore = KeyStore2.getInstance();
1133         mNamespace = KeyProperties.NAMESPACE_APPLICATION;
1134     }
1135 
1136     @Override
engineLoad(LoadStoreParameter param)1137     public void engineLoad(LoadStoreParameter param) throws IOException,
1138             NoSuchAlgorithmException, CertificateException {
1139         @KeyProperties.Namespace int namespace = KeyProperties.NAMESPACE_APPLICATION;
1140         if (param != null) {
1141             if (param instanceof AndroidKeyStoreLoadStoreParameter) {
1142                 namespace = ((AndroidKeyStoreLoadStoreParameter) param).getNamespace();
1143             } else {
1144                 throw new IllegalArgumentException(
1145                         "Unsupported param type: " + param.getClass());
1146             }
1147         }
1148         mKeyStore = KeyStore2.getInstance();
1149         mNamespace = namespace;
1150     }
1151 
1152     @Override
engineSetEntry(String alias, Entry entry, ProtectionParameter param)1153     public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
1154             throws KeyStoreException {
1155         if (entry == null) {
1156             throw new KeyStoreException("entry == null");
1157         }
1158 
1159         if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
1160             java.security.KeyStore.TrustedCertificateEntry trE =
1161                     (java.security.KeyStore.TrustedCertificateEntry) entry;
1162             // engineSetCertificateEntry does not overwrite if the existing entry
1163             // is a key entry, but the semantic of engineSetEntry is such that it
1164             // overwrites any existing entry. Thus we delete any possible existing
1165             // entry by this alias.
1166             engineDeleteEntry(alias);
1167             engineSetCertificateEntry(alias, trE.getTrustedCertificate());
1168             return;
1169         }
1170 
1171         if (entry instanceof PrivateKeyEntry) {
1172             PrivateKeyEntry prE = (PrivateKeyEntry) entry;
1173             setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param);
1174         } else if (entry instanceof SecretKeyEntry) {
1175             SecretKeyEntry secE = (SecretKeyEntry) entry;
1176             setSecretKeyEntry(alias, secE.getSecretKey(), param);
1177         } else if (entry instanceof WrappedKeyEntry) {
1178             WrappedKeyEntry wke = (WrappedKeyEntry) entry;
1179             setWrappedKeyEntry(alias, wke, param);
1180         } else {
1181             throw new KeyStoreException(
1182                     "Entry must be a PrivateKeyEntry, SecretKeyEntry, WrappedKeyEntry "
1183                             + "or TrustedCertificateEntry; was " + entry);
1184         }
1185     }
1186 }
1187