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