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.equals(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         @Domain int targetDomain = (getTargetDomain());
605 
606         if (key instanceof AndroidKeyStoreSecretKey) {
607             String keyAliasInKeystore =
608                     ((AndroidKeyStoreSecretKey) key).getUserKeyDescriptor().alias;
609 
610             KeyDescriptor descriptor = ((AndroidKeyStoreSecretKey) key).getUserKeyDescriptor();
611 
612             // This throws if the request cannot replace the existing key.
613             assertCanReplace(alias, targetDomain, mNamespace, descriptor);
614 
615             // This is the entry where this key is already stored. No need to do anything.
616             if (params != null) {
617                 throw new KeyStoreException("Modifying KeyStore-backed key using protection"
618                         + " parameters not supported");
619             }
620             return;
621         }
622 
623         if (params == null) {
624             throw new KeyStoreException(
625                     "Protection parameters must be specified when importing a symmetric key");
626         }
627 
628         // Not a KeyStore-backed secret key -- import its key material into keystore.
629         String keyExportFormat = key.getFormat();
630         if (keyExportFormat == null) {
631             throw new KeyStoreException(
632                     "Only secret keys that export their key material are supported");
633         } else if (!"RAW".equals(keyExportFormat)) {
634             throw new KeyStoreException(
635                     "Unsupported secret key material export format: " + keyExportFormat);
636         }
637         byte[] keyMaterial = key.getEncoded();
638         if (keyMaterial == null) {
639             throw new KeyStoreException("Key did not export its key material despite supporting"
640                     + " RAW format export");
641         }
642 
643         final List<KeyParameter> importArgs = new ArrayList<>();
644 
645         try {
646             int keymasterAlgorithm =
647                     KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(
648                             key.getAlgorithm());
649 
650             importArgs.add(KeyStore2ParameterUtils.makeEnum(
651                     KeymasterDefs.KM_TAG_ALGORITHM,
652                     keymasterAlgorithm
653             ));
654 
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 
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 "
681                                         + key.getAlgorithm());
682                     }
683                 }
684                 int outputBits = KeymasterUtils.getDigestOutputSizeBits(keymasterImpliedDigest);
685                 if (outputBits == -1) {
686                     throw new ProviderException(
687                             "HMAC key authorized for unsupported digest: "
688                                     + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest));
689                 }
690                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
691                         KeymasterDefs.KM_TAG_DIGEST, keymasterImpliedDigest
692                 ));
693                 importArgs.add(KeyStore2ParameterUtils.makeInt(
694                         KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, outputBits
695                 ));
696             } else {
697                 if (params.isDigestsSpecified()) {
698                     for (String digest : params.getDigests()) {
699                         importArgs.add(KeyStore2ParameterUtils.makeEnum(
700                                 KeymasterDefs.KM_TAG_DIGEST,
701                                 KeyProperties.Digest.toKeymaster(digest)
702                         ));
703                     }
704                 }
705             }
706 
707             KeyStore2ParameterUtils.forEachSetFlag(params.getPurposes(), (purpose) -> {
708                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
709                         KeymasterDefs.KM_TAG_PURPOSE,
710                         KeyProperties.Purpose.toKeymaster(purpose)
711                 ));
712             });
713 
714             boolean indCpa = false;
715             if ((params.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0) {
716                 if (((KeyProtection) param).isRandomizedEncryptionRequired()) {
717                     indCpa = true;
718                 } else {
719                     importArgs.add(KeyStore2ParameterUtils.makeBool(
720                             KeymasterDefs.KM_TAG_CALLER_NONCE
721                     ));
722                 }
723             }
724 
725             for (String blockMode : params.getBlockModes()) {
726                 int keymasterBlockMode = KeyProperties.BlockMode.toKeymaster(blockMode);
727                 if (indCpa
728                         && !KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
729                                 keymasterBlockMode)) {
730                     throw new KeyStoreException(
731                             "Randomized encryption (IND-CPA) required but may be violated by"
732                                     + " block mode: " + blockMode
733                                     + ". See KeyProtection documentation.");
734 
735                 }
736                 if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES
737                         && keymasterBlockMode == KeymasterDefs.KM_MODE_GCM) {
738                     importArgs.add(KeyStore2ParameterUtils.makeInt(
739                             KeymasterDefs.KM_TAG_MIN_MAC_LENGTH,
740                             AndroidKeyStoreAuthenticatedAESCipherSpi.GCM
741                                     .MIN_SUPPORTED_TAG_LENGTH_BITS
742                     ));
743                 }
744                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
745                         KeymasterDefs.KM_TAG_BLOCK_MODE,
746                         keymasterBlockMode
747                 ));
748             }
749 
750             if (params.getSignaturePaddings().length > 0) {
751                 throw new KeyStoreException("Signature paddings not supported for symmetric keys");
752             }
753 
754             for (String padding : params.getEncryptionPaddings()) {
755                 importArgs.add(KeyStore2ParameterUtils.makeEnum(
756                         KeymasterDefs.KM_TAG_PADDING,
757                         KeyProperties.EncryptionPadding.toKeymaster(padding)
758                 ));
759             }
760 
761             KeyStore2ParameterUtils.addUserAuthArgs(importArgs, params);
762 
763             if (params.getKeyValidityStart() != null) {
764                 importArgs.add(KeyStore2ParameterUtils.makeDate(
765                         KeymasterDefs.KM_TAG_ACTIVE_DATETIME, params.getKeyValidityStart()
766                 ));
767             }
768             if (params.getKeyValidityForOriginationEnd() != null) {
769                 importArgs.add(KeyStore2ParameterUtils.makeDate(
770                         KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
771                         params.getKeyValidityForOriginationEnd()
772                 ));
773             }
774             if (params.getKeyValidityForConsumptionEnd() != null) {
775                 importArgs.add(KeyStore2ParameterUtils.makeDate(
776                         KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
777                         params.getKeyValidityForConsumptionEnd()
778                 ));
779             }
780             if (params.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
781                 importArgs.add(KeyStore2ParameterUtils.makeInt(
782                         KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
783                         params.getMaxUsageCount()
784                 ));
785             }
786         } catch (IllegalArgumentException | IllegalStateException e) {
787             throw new KeyStoreException(e);
788         }
789 
790         int flags = 0;
791         if (params.isCriticalToDeviceEncryption()) {
792             flags |= IKeystoreSecurityLevel.KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING;
793         }
794 
795         @SecurityLevel int securityLevel = params.isStrongBoxBacked() ? SecurityLevel.STRONGBOX :
796                 SecurityLevel.TRUSTED_ENVIRONMENT;
797 
798         try {
799             KeyStoreSecurityLevel securityLevelInterface = mKeyStore.getSecurityLevel(
800                     securityLevel);
801 
802             KeyDescriptor descriptor = makeKeyDescriptor(alias);
803 
804             securityLevelInterface.importKey(descriptor, null /* TODO attestationKey */,
805                     importArgs, flags, keyMaterial);
806         } catch (android.security.KeyStoreException e) {
807             throw new KeyStoreException("Failed to import secret key.", e);
808         }
809     }
810 
setWrappedKeyEntry(String alias, WrappedKeyEntry entry, java.security.KeyStore.ProtectionParameter param)811     private void setWrappedKeyEntry(String alias, WrappedKeyEntry entry,
812             java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
813         if (param != null) {
814             throw new KeyStoreException("Protection parameters are specified inside wrapped keys");
815         }
816 
817         byte[] maskingKey = new byte[32];
818 
819         String[] parts = entry.getTransformation().split("/");
820 
821         List<KeyParameter> args = new ArrayList<>();
822 
823         String algorithm = parts[0];
824         if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
825             args.add(KeyStore2ParameterUtils.makeEnum(
826                     KeymasterDefs.KM_TAG_ALGORITHM,
827                     KeymasterDefs.KM_ALGORITHM_RSA
828             ));
829         } else {
830             throw new KeyStoreException("Algorithm \"" + algorithm + "\" not supported for "
831                     + "wrapping. Only RSA wrapping keys are supported.");
832         }
833 
834         if (parts.length > 1) {
835             String mode = parts[1];
836             args.add(KeyStore2ParameterUtils.makeEnum(
837                     KeymasterDefs.KM_TAG_BLOCK_MODE,
838                     KeyProperties.BlockMode.toKeymaster(mode)
839             ));
840         }
841 
842         if (parts.length > 2) {
843             @KeyProperties.EncryptionPaddingEnum int padding =
844                     KeyProperties.EncryptionPadding.toKeymaster(parts[2]);
845             if (padding != KeymasterDefs.KM_PAD_NONE) {
846                 args.add(KeyStore2ParameterUtils.makeEnum(
847                         KeymasterDefs.KM_TAG_PADDING,
848                         padding
849                 ));
850             }
851         }
852 
853         KeyGenParameterSpec spec = (KeyGenParameterSpec) entry.getAlgorithmParameterSpec();
854         if (spec.isDigestsSpecified()) {
855             @KeyProperties.DigestEnum int digest =
856                     KeyProperties.Digest.toKeymaster(spec.getDigests()[0]);
857             if (digest != KeymasterDefs.KM_DIGEST_NONE) {
858                 args.add(KeyStore2ParameterUtils.makeEnum(
859                         KeymasterDefs.KM_TAG_DIGEST,
860                         digest
861                 ));
862             }
863         }
864 
865         KeyDescriptor wrappingkey = makeKeyDescriptor(entry.getWrappingKeyAlias());
866 
867         KeyEntryResponse response = null;
868         try {
869             response = mKeyStore.getKeyEntry(wrappingkey);
870         } catch (android.security.KeyStoreException e) {
871             throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
872                     + e.getErrorCode(), e);
873         }
874 
875         KeyDescriptor wrappedKey = makeKeyDescriptor(alias);
876 
877         KeyStoreSecurityLevel securityLevel = new KeyStoreSecurityLevel(response.iSecurityLevel);
878 
879         final BiometricManager bm = android.app.AppGlobals.getInitialApplication()
880                 .getSystemService(BiometricManager.class);
881 
882         long[] biometricSids = bm.getAuthenticatorIds();
883 
884         List<AuthenticatorSpec> authenticatorSpecs = new ArrayList<>();
885 
886         AuthenticatorSpec authenticatorSpec = new AuthenticatorSpec();
887         authenticatorSpec.authenticatorType = HardwareAuthenticatorType.PASSWORD;
888         authenticatorSpec.authenticatorId = GateKeeper.getSecureUserId();
889         authenticatorSpecs.add(authenticatorSpec);
890 
891         for (long sid : biometricSids) {
892             AuthenticatorSpec authSpec = new AuthenticatorSpec();
893             authSpec.authenticatorType = HardwareAuthenticatorType.FINGERPRINT;
894             authSpec.authenticatorId = sid;
895             authenticatorSpecs.add(authSpec);
896         }
897 
898         try {
899             securityLevel.importWrappedKey(
900                     wrappedKey, wrappingkey,
901                     entry.getWrappedKeyBytes(),
902                     null /* masking key is set to 32 bytes if null is given here */,
903                     args,
904                     authenticatorSpecs.toArray(new AuthenticatorSpec[0]));
905         } catch (android.security.KeyStoreException e) {
906             switch (e.getErrorCode()) {
907                 case KeymasterDefs.KM_ERROR_UNIMPLEMENTED: {
908                     throw new SecureKeyImportUnavailableException("Could not import wrapped key");
909                 }
910                 default:
911                     throw new KeyStoreException("Failed to import wrapped key. Keystore error "
912                             + "code: " + e.getErrorCode(), e);
913             }
914         }
915     }
916 
917     @Override
engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)918     public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
919             throws KeyStoreException {
920         throw new KeyStoreException("Operation not supported because key encoding is unknown");
921     }
922 
923     /**
924      * This function sets a trusted certificate entry. It fails if the given
925      * alias is already taken by an actual key entry. However, if the entry is a
926      * trusted certificate it will get silently replaced.
927      * @param alias the alias name
928      * @param cert the certificate
929      *
930      * @throws KeyStoreException if the alias is already taken by a secret or private
931      *         key entry.
932      * @throws KeyStoreException with a nested {@link CertificateEncodingException}
933      *         if the {@code cert.getEncoded()} throws.
934      * @throws KeyStoreException with a nested {@link android.security.KeyStoreException} if
935      *         something went wrong while inserting the certificate into keystore.
936      * @throws NullPointerException if cert or alias is null.
937      *
938      * @hide
939      */
940     @Override
engineSetCertificateEntry(String alias, Certificate cert)941     public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
942         if (isKeyEntry(alias)) {
943             throw new KeyStoreException("Entry exists and is not a trusted certificate");
944         }
945 
946         // We can't set something to null.
947         if (cert == null) {
948             throw new NullPointerException("cert == null");
949         }
950 
951         final byte[] encoded;
952         try {
953             encoded = cert.getEncoded();
954         } catch (CertificateEncodingException e) {
955             throw new KeyStoreException(e);
956         }
957 
958         try {
959             mKeyStore.updateSubcomponents(makeKeyDescriptor(alias),
960                     null /* publicCert - unused when used as pure certificate store. */,
961                     encoded);
962         } catch (android.security.KeyStoreException e) {
963             throw new KeyStoreException("Couldn't insert certificate.", e);
964         }
965     }
966 
967     @Override
engineDeleteEntry(String alias)968     public void engineDeleteEntry(String alias) throws KeyStoreException {
969         KeyDescriptor descriptor = makeKeyDescriptor(alias);
970         try {
971             mKeyStore.deleteKey(descriptor);
972         } catch (android.security.KeyStoreException e) {
973             if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
974                 throw new KeyStoreException("Failed to delete entry: " + alias, e);
975             }
976         }
977     }
978 
getUniqueAliases()979     private Set<String> getUniqueAliases() {
980         try {
981             final KeyDescriptor[] keys = mKeyStore.list(
982                     getTargetDomain(),
983                     mNamespace
984             );
985             final Set<String> aliases = new HashSet<>(keys.length);
986             for (KeyDescriptor d : keys) {
987                 aliases.add(d.alias);
988             }
989             return aliases;
990         } catch (android.security.KeyStoreException e) {
991             Log.e(TAG, "Failed to list keystore entries.", e);
992             return new HashSet<>();
993         }
994     }
995 
996     @Override
engineAliases()997     public Enumeration<String> engineAliases() {
998         return Collections.enumeration(getUniqueAliases());
999     }
1000 
1001     @Override
engineContainsAlias(String alias)1002     public boolean engineContainsAlias(String alias) {
1003         if (alias == null) {
1004             throw new NullPointerException("alias == null");
1005         }
1006 
1007         return getKeyMetadata(alias) != null;
1008     }
1009 
1010     @Override
engineSize()1011     public int engineSize() {
1012         return getUniqueAliases().size();
1013     }
1014 
1015     @Override
engineIsKeyEntry(String alias)1016     public boolean engineIsKeyEntry(String alias) {
1017         return isKeyEntry(alias);
1018     }
1019 
isKeyEntry(String alias)1020     private boolean isKeyEntry(String alias) {
1021         if (alias == null) {
1022             throw new NullPointerException("alias == null");
1023         }
1024 
1025         KeyEntryResponse response = getKeyMetadata(alias);
1026         // If response is null, there is no such entry.
1027         // If response.iSecurityLevel is null, there is no private or secret key material stored.
1028         return response != null && response.iSecurityLevel != null;
1029     }
1030 
1031 
1032     @Override
engineIsCertificateEntry(String alias)1033     public boolean engineIsCertificateEntry(String alias) {
1034         if (alias == null) {
1035             throw new NullPointerException("alias == null");
1036         }
1037         KeyEntryResponse response = getKeyMetadata(alias);
1038         // If response == null there is no such entry.
1039         // If there is no certificateChain, then this is not a certificate entry.
1040         // If there is a private key entry, this is the certificate chain for that
1041         // key entry and not a CA certificate entry.
1042         return response != null
1043                 && response.metadata.certificateChain != null
1044                 && response.iSecurityLevel == null;
1045     }
1046 
1047     @Override
engineGetCertificateAlias(Certificate cert)1048     public String engineGetCertificateAlias(Certificate cert) {
1049         if (cert == null) {
1050             return null;
1051         }
1052         if (!"X.509".equalsIgnoreCase(cert.getType())) {
1053             Log.e(TAG, "In engineGetCertificateAlias: only X.509 certificates are supported.");
1054             return null;
1055         }
1056         byte[] targetCertBytes;
1057         try {
1058             targetCertBytes = cert.getEncoded();
1059         } catch (CertificateEncodingException e) {
1060             Log.e(TAG, "While trying to get the alias for a certificate.", e);
1061             return null;
1062         }
1063         if (targetCertBytes == null) {
1064             return null;
1065         }
1066 
1067         KeyDescriptor[] keyDescriptors = null;
1068         try {
1069             keyDescriptors = mKeyStore.list(
1070                     getTargetDomain(),
1071                     mNamespace
1072             );
1073         } catch (android.security.KeyStoreException e) {
1074             Log.w(TAG, "Failed to get list of keystore entries.", e);
1075         }
1076 
1077         String caAlias = null;
1078         for (KeyDescriptor d : keyDescriptors) {
1079             KeyEntryResponse response = getKeyMetadata(d.alias);
1080             if (response == null) {
1081                 continue;
1082             }
1083             /*
1084              * The KeyStoreSpi documentation says to only compare the first certificate in the
1085              * chain which is equivalent to the {@code response.metadata.certificate} field.
1086              * So we look for a hit in this field first. For pure CA certificate entries,
1087              * we check the {@code response.metadata.certificateChain} field. But we only
1088              * return a CA alias if there was no hit in the certificate field of any other
1089              * entry.
1090              */
1091             if (response.metadata.certificate != null) {
1092                 if (Arrays.equals(response.metadata.certificate, targetCertBytes)) {
1093                     return d.alias;
1094                 }
1095             } else if (response.metadata.certificateChain != null && caAlias == null) {
1096                 if (Arrays.equals(response.metadata.certificateChain, targetCertBytes)) {
1097                     caAlias =  d.alias;
1098                 }
1099             }
1100         }
1101         return caAlias;
1102     }
1103 
1104     /**
1105      * Used by Tests to initialize with a fake KeyStore2.
1106      * @hide
1107      * @param keystore
1108      */
1109     @VisibleForTesting
initForTesting(KeyStore2 keystore)1110     public void initForTesting(KeyStore2 keystore) {
1111         mKeyStore = keystore;
1112         mNamespace = KeyProperties.NAMESPACE_APPLICATION;
1113     }
1114 
1115     @Override
engineStore(OutputStream stream, char[] password)1116     public void engineStore(OutputStream stream, char[] password) throws IOException,
1117             NoSuchAlgorithmException, CertificateException {
1118         throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
1119     }
1120 
1121     @Override
engineLoad(InputStream stream, char[] password)1122     public void engineLoad(InputStream stream, char[] password) throws IOException,
1123             NoSuchAlgorithmException, CertificateException {
1124         if (stream != null) {
1125             throw new IllegalArgumentException("InputStream not supported");
1126         }
1127 
1128         if (password != null) {
1129             throw new IllegalArgumentException("password not supported");
1130         }
1131 
1132         // Unfortunate name collision.
1133         mKeyStore = KeyStore2.getInstance();
1134         mNamespace = KeyProperties.NAMESPACE_APPLICATION;
1135     }
1136 
1137     @Override
engineLoad(LoadStoreParameter param)1138     public void engineLoad(LoadStoreParameter param) throws IOException,
1139             NoSuchAlgorithmException, CertificateException {
1140         @KeyProperties.Namespace int namespace = KeyProperties.NAMESPACE_APPLICATION;
1141         if (param != null) {
1142             if (param instanceof AndroidKeyStoreLoadStoreParameter) {
1143                 namespace = ((AndroidKeyStoreLoadStoreParameter) param).getNamespace();
1144             } else {
1145                 throw new IllegalArgumentException(
1146                         "Unsupported param type: " + param.getClass());
1147             }
1148         }
1149         mKeyStore = KeyStore2.getInstance();
1150         mNamespace = namespace;
1151     }
1152 
1153     @Override
engineSetEntry(String alias, Entry entry, ProtectionParameter param)1154     public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
1155             throws KeyStoreException {
1156         if (entry == null) {
1157             throw new KeyStoreException("entry == null");
1158         }
1159 
1160         if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
1161             java.security.KeyStore.TrustedCertificateEntry trE =
1162                     (java.security.KeyStore.TrustedCertificateEntry) entry;
1163             // engineSetCertificateEntry does not overwrite if the existing entry
1164             // is a key entry, but the semantic of engineSetEntry is such that it
1165             // overwrites any existing entry. Thus we delete any possible existing
1166             // entry by this alias.
1167             engineDeleteEntry(alias);
1168             engineSetCertificateEntry(alias, trE.getTrustedCertificate());
1169             return;
1170         }
1171 
1172         if (entry instanceof PrivateKeyEntry) {
1173             PrivateKeyEntry prE = (PrivateKeyEntry) entry;
1174             setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param);
1175         } else if (entry instanceof SecretKeyEntry) {
1176             SecretKeyEntry secE = (SecretKeyEntry) entry;
1177             setSecretKeyEntry(alias, secE.getSecretKey(), param);
1178         } else if (entry instanceof WrappedKeyEntry) {
1179             WrappedKeyEntry wke = (WrappedKeyEntry) entry;
1180             setWrappedKeyEntry(alias, wke, param);
1181         } else {
1182             throw new KeyStoreException(
1183                     "Entry must be a PrivateKeyEntry, SecretKeyEntry, WrappedKeyEntry "
1184                             + "or TrustedCertificateEntry; was " + entry);
1185         }
1186     }
1187 }
1188