• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.security.keystore2;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.security.KeyStore2;
22 import android.security.KeyStoreSecurityLevel;
23 import android.security.keymaster.KeymasterDefs;
24 import android.security.keystore.KeyPermanentlyInvalidatedException;
25 import android.security.keystore.KeyProperties;
26 import android.security.keystore.KeyStoreCryptoOperation;
27 import android.system.keystore2.Authorization;
28 import android.system.keystore2.Domain;
29 import android.system.keystore2.KeyDescriptor;
30 import android.system.keystore2.KeyEntryResponse;
31 import android.system.keystore2.KeyMetadata;
32 import android.system.keystore2.ResponseCode;
33 
34 import java.security.KeyPair;
35 import java.security.Provider;
36 import java.security.ProviderException;
37 import java.security.PublicKey;
38 import java.security.Security;
39 import java.security.Signature;
40 import java.security.UnrecoverableKeyException;
41 import java.security.cert.X509Certificate;
42 import java.security.interfaces.ECPublicKey;
43 import java.security.interfaces.RSAPublicKey;
44 
45 import javax.crypto.Cipher;
46 import javax.crypto.KeyAgreement;
47 import javax.crypto.Mac;
48 import javax.crypto.SecretKey;
49 
50 /**
51  * A provider focused on providing JCA interfaces for the Android KeyStore.
52  *
53  * @hide
54  */
55 public class AndroidKeyStoreProvider extends Provider {
56     private static final String PROVIDER_NAME = "AndroidKeyStore";
57 
58     // IMPLEMENTATION NOTE: Class names are hard-coded in this provider to avoid loading these
59     // classes when this provider is instantiated and installed early on during each app's
60     // initialization process.
61     //
62     // Crypto operations operating on the AndroidKeyStore keys must not be offered by this provider.
63     // Instead, they need to be offered by AndroidKeyStoreBCWorkaroundProvider. See its Javadoc
64     // for details.
65 
66     private static final String PACKAGE_NAME = "android.security.keystore2";
67 
68     private static final String DESEDE_SYSTEM_PROPERTY =
69             "ro.hardware.keystore_desede";
70 
71     // Conscrypt returns the Ed25519 OID as the JCA key algorithm.
72     private static final String ED25519_OID = "1.3.101.112";
73     // Conscrypt returns "XDH" as the X25519 JCA key algorithm.
74     private static final String X25519_ALIAS = "XDH";
75 
76     /** @hide **/
AndroidKeyStoreProvider()77     public AndroidKeyStoreProvider() {
78         super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");
79 
80         boolean supports3DES = "true".equals(android.os.SystemProperties.get(DESEDE_SYSTEM_PROPERTY));
81 
82         // java.security.KeyStore
83         put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi");
84 
85         // java.security.KeyPairGenerator
86         put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
87         put("KeyPairGenerator.RSA", PACKAGE_NAME +  ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
88         put("KeyPairGenerator.XDH", PACKAGE_NAME +  ".AndroidKeyStoreKeyPairGeneratorSpi$XDH");
89         put("KeyPairGenerator.ED25519", PACKAGE_NAME
90                 +  ".AndroidKeyStoreKeyPairGeneratorSpi$ED25519");
91 
92         // java.security.KeyFactory
93         putKeyFactoryImpl("EC");
94         putKeyFactoryImpl("RSA");
95         putKeyFactoryImpl("XDH");
96         putKeyFactoryImpl("ED25519");
97 
98         // javax.crypto.KeyGenerator
99         put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
100         put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1");
101         put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA224");
102         put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA256");
103         put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA384");
104         put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA512");
105 
106         if (supports3DES) {
107             put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede");
108         }
109 
110         // javax.crypto.KeyAgreement
111         put("KeyAgreement.ECDH", PACKAGE_NAME + ".AndroidKeyStoreKeyAgreementSpi$ECDH");
112         put("KeyAgreement.XDH", PACKAGE_NAME + ".AndroidKeyStoreKeyAgreementSpi$XDH");
113 
114         // java.security.SecretKeyFactory
115         putSecretKeyFactoryImpl("AES");
116         if (supports3DES) {
117             putSecretKeyFactoryImpl("DESede");
118         }
119         putSecretKeyFactoryImpl("HmacSHA1");
120         putSecretKeyFactoryImpl("HmacSHA224");
121         putSecretKeyFactoryImpl("HmacSHA256");
122         putSecretKeyFactoryImpl("HmacSHA384");
123         putSecretKeyFactoryImpl("HmacSHA512");
124     }
125 
126     /**
127      * Installs a new instance of this provider (and the
128      * {@link AndroidKeyStoreBCWorkaroundProvider}).
129      * @hide
130      */
install()131     public static void install() {
132         Provider[] providers = Security.getProviders();
133         int bcProviderIndex = -1;
134         for (int i = 0; i < providers.length; i++) {
135             Provider provider = providers[i];
136             if ("BC".equals(provider.getName())) {
137                 bcProviderIndex = i;
138                 break;
139             }
140         }
141 
142         Security.addProvider(new AndroidKeyStoreProvider());
143         Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider();
144         if (bcProviderIndex != -1) {
145             // Bouncy Castle provider found -- install the workaround provider above it.
146             // insertProviderAt uses 1-based positions.
147             Security.insertProviderAt(workaroundProvider, bcProviderIndex + 1);
148         } else {
149             // Bouncy Castle provider not found -- install the workaround provider at lowest
150             // priority.
151             Security.addProvider(workaroundProvider);
152         }
153     }
154 
putSecretKeyFactoryImpl(String algorithm)155     private void putSecretKeyFactoryImpl(String algorithm) {
156         put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreSecretKeyFactorySpi");
157     }
158 
putKeyFactoryImpl(String algorithm)159     private void putKeyFactoryImpl(String algorithm) {
160         put("KeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreKeyFactorySpi");
161     }
162 
163     /**
164      * Gets the Android KeyStore operation handle corresponding to the provided JCA crypto
165      * primitive.
166      *
167      * <p>The following primitives are supported: {@link Cipher}, {@link Signature} and {@link Mac}.
168      *
169      * @return Android KeyStore operation handle or {@code 0} if the provided primitive's Android
170      *         KeyStore operation is not in progress.
171      *
172      * @throws IllegalArgumentException if the provided primitive is not supported or is not backed
173      *         by AndroidKeyStore provider.
174      * @throws IllegalStateException if the provided primitive is not initialized.
175      * @hide
176      */
getKeyStoreOperationHandle(Object cryptoPrimitive)177     public static long getKeyStoreOperationHandle(Object cryptoPrimitive) {
178         if (cryptoPrimitive == null) {
179             throw new NullPointerException();
180         }
181         Object spi;
182         if (cryptoPrimitive instanceof Signature) {
183             spi = ((Signature) cryptoPrimitive).getCurrentSpi();
184         } else if (cryptoPrimitive instanceof Mac) {
185             spi = ((Mac) cryptoPrimitive).getCurrentSpi();
186         } else if (cryptoPrimitive instanceof Cipher) {
187             spi = ((Cipher) cryptoPrimitive).getCurrentSpi();
188         } else if (cryptoPrimitive instanceof KeyAgreement) {
189             spi = ((KeyAgreement) cryptoPrimitive).getCurrentSpi();
190         } else {
191             throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive
192                     + ". Supported: Signature, Mac, Cipher");
193         }
194         if (spi == null) {
195             throw new IllegalStateException("Crypto primitive not initialized");
196         } else if (!(spi instanceof KeyStoreCryptoOperation)) {
197             throw new IllegalArgumentException(
198                     "Crypto primitive not backed by AndroidKeyStore provider: " + cryptoPrimitive
199                     + ", spi: " + spi);
200         }
201         return ((KeyStoreCryptoOperation) spi).getOperationHandle();
202     }
203 
204     /**
205      * This helper function gets called if the key loaded from the keystore daemon
206      * is for an asymmetric algorithm. It constructs an instance of {@link AndroidKeyStorePublicKey}
207      * which implements {@link PublicKey}.
208      *
209      * @param descriptor The original key descriptor that was used to load the key.
210      *
211      * @param metadata The key metadata which includes the public key material, a reference to the
212      *                 stored private key material, the key characteristics.
213      * @param iSecurityLevel A binder interface that allows using the private key.
214      * @param algorithm Must indicate EC or RSA.
215      * @return AndroidKeyStorePublicKey
216      * @throws UnrecoverableKeyException
217      * @hide
218      */
219     @NonNull
makeAndroidKeyStorePublicKeyFromKeyEntryResponse( @onNull KeyDescriptor descriptor, @NonNull KeyMetadata metadata, @NonNull KeyStoreSecurityLevel iSecurityLevel, int algorithm)220     static AndroidKeyStorePublicKey makeAndroidKeyStorePublicKeyFromKeyEntryResponse(
221             @NonNull KeyDescriptor descriptor,
222             @NonNull KeyMetadata metadata,
223             @NonNull KeyStoreSecurityLevel iSecurityLevel, int algorithm)
224             throws UnrecoverableKeyException {
225         if (metadata.certificate == null) {
226             throw new UnrecoverableKeyException("Failed to obtain X.509 form of public key."
227                     + " Keystore has no public certificate stored.");
228         }
229         final byte[] x509PublicCert = metadata.certificate;
230 
231         final X509Certificate parsedX509Certificate =
232                 AndroidKeyStoreSpi.toCertificate(x509PublicCert);
233         if (parsedX509Certificate == null) {
234             throw new UnrecoverableKeyException("Failed to parse the X.509 certificate containing"
235                    + " the public key. This likely indicates a hardware problem.");
236         }
237 
238         PublicKey publicKey = parsedX509Certificate.getPublicKey();
239 
240         String jcaKeyAlgorithm = publicKey.getAlgorithm();
241 
242         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) {
243             return new AndroidKeyStoreECPublicKey(descriptor, metadata,
244                     iSecurityLevel, (ECPublicKey) publicKey);
245         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(jcaKeyAlgorithm)) {
246             return new AndroidKeyStoreRSAPublicKey(descriptor, metadata,
247                     iSecurityLevel, (RSAPublicKey) publicKey);
248         } else if (ED25519_OID.equalsIgnoreCase(jcaKeyAlgorithm)) {
249             final byte[] publicKeyEncoded = publicKey.getEncoded();
250             return new AndroidKeyStoreEdECPublicKey(descriptor, metadata, ED25519_OID,
251                     iSecurityLevel, publicKeyEncoded);
252         } else if (X25519_ALIAS.equalsIgnoreCase(jcaKeyAlgorithm)) {
253             return new AndroidKeyStoreXDHPublicKey(descriptor, metadata, X25519_ALIAS,
254                     iSecurityLevel, publicKey.getEncoded());
255         } else {
256             throw new ProviderException("Unsupported Android Keystore public key algorithm: "
257                     + jcaKeyAlgorithm);
258         }
259     }
260 
261     /** @hide **/
262     @NonNull
loadAndroidKeyStorePublicKeyFromKeystore( @onNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace)263     public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
264             @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace)
265             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
266         AndroidKeyStoreKey key =
267                 loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace);
268         if (key instanceof AndroidKeyStorePublicKey) {
269             return (AndroidKeyStorePublicKey) key;
270         } else {
271             throw new UnrecoverableKeyException("No asymmetric key found by the given alias.");
272         }
273     }
274 
275     /** @hide **/
276     @NonNull
loadAndroidKeyStoreKeyPairFromKeystore( @onNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)277     public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
278             @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)
279             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
280         AndroidKeyStoreKey key =
281                 loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor);
282         if (key instanceof AndroidKeyStorePublicKey) {
283             AndroidKeyStorePublicKey publicKey = (AndroidKeyStorePublicKey) key;
284             return new KeyPair(publicKey, publicKey.getPrivateKey());
285         } else {
286             throw new UnrecoverableKeyException("No asymmetric key found by the given alias.");
287         }
288     }
289 
290     /** @hide **/
291     @NonNull
loadAndroidKeyStorePrivateKeyFromKeystore( @onNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace)292     public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
293             @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace)
294             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
295         AndroidKeyStoreKey key =
296                 loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace);
297         if (key instanceof AndroidKeyStorePublicKey) {
298             return ((AndroidKeyStorePublicKey) key).getPrivateKey();
299         } else {
300             throw new UnrecoverableKeyException("No asymmetric key found by the given alias.");
301         }
302     }
303 
304     /** @hide **/
305     @NonNull
loadAndroidKeyStoreSecretKeyFromKeystore( @onNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)306     public static SecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
307             @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)
308             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
309 
310         AndroidKeyStoreKey key =
311                 loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor);
312         if (key instanceof SecretKey) {
313             return (SecretKey) key;
314         } else {
315             throw new UnrecoverableKeyException("No secret key found by the given alias.");
316         }
317     }
318 
319     @NonNull
makeAndroidKeyStoreSecretKeyFromKeyEntryResponse( @onNull KeyDescriptor descriptor, @NonNull KeyEntryResponse response, int algorithm, int digest)320     private static AndroidKeyStoreSecretKey makeAndroidKeyStoreSecretKeyFromKeyEntryResponse(
321             @NonNull KeyDescriptor descriptor,
322             @NonNull KeyEntryResponse response, int algorithm, int digest)
323             throws UnrecoverableKeyException {
324         @KeyProperties.KeyAlgorithmEnum String keyAlgorithmString;
325         try {
326             keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
327                     algorithm, digest);
328         } catch (IllegalArgumentException e) {
329             throw (UnrecoverableKeyException)
330                     new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
331         }
332 
333         return new AndroidKeyStoreSecretKey(descriptor,
334                 response.metadata, keyAlgorithmString,
335                 new KeyStoreSecurityLevel(response.iSecurityLevel));
336     }
337 
338     /**
339      * Loads an AndroidKeyStoreKey from the AndroidKeyStore backend.
340      *
341      * @param keyStore The keystore2 backend.
342      * @param alias The alias of the key in the Keystore database.
343      * @param namespace The Keystore namespace. This is used by system api only to request
344      *         Android system specific keystore namespace, which can be configured
345      *         in the device's SEPolicy. Third party apps and most system components
346      *         set this parameter to -1 to indicate their application specific namespace.
347      *         See <a href="https://source.android.com/security/keystore#access-control">
348      *             Keystore 2.0 access control</a>
349      * @hide
350      **/
351     @NonNull
loadAndroidKeyStoreKeyFromKeystore( @onNull KeyStore2 keyStore, @NonNull String alias, int namespace)352     public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
353             @NonNull KeyStore2 keyStore, @NonNull String alias, int namespace)
354             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
355         int descriptorNamespace;
356         int descriptorDomain;
357         if (namespace == KeyProperties.NAMESPACE_APPLICATION) {
358             descriptorNamespace = KeyProperties.NAMESPACE_APPLICATION; // ignored;
359             descriptorDomain = Domain.APP;
360         } else {
361             descriptorNamespace = namespace;
362             descriptorDomain = Domain.SELINUX;
363         }
364         return loadAndroidKeyStoreKeyFromKeystore(keyStore, alias, descriptorNamespace,
365                 descriptorDomain);
366     }
367 
368     /**
369      * Loads an AndroidKeyStoreKey from the AndroidKeyStore backend.
370      *
371      * @param keyStore The keystore2 backend
372      * @param alias The alias of the key in the Keystore database
373      * @param namespace The Keystore namespace. This is used by system api only to request
374      *         Android system specific keystore namespace, which can be configured
375      *         in the device's SEPolicy. Third party apps and most system components
376      *         set this parameter to -1 to indicate their application specific namespace.
377      *         See <a href="https://source.android.com/security/keystore#access-control">
378      *             Keystore 2.0 access control</a>
379      * @param domain The Keystore domain
380      * @return an AndroidKeyStoreKey corresponding to the provided values for the KeyDescriptor
381      * @hide
382      */
loadAndroidKeyStoreKeyFromKeystore(@onNull KeyStore2 keyStore, @Nullable String alias, long namespace, int domain)383     public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(@NonNull KeyStore2 keyStore,
384             @Nullable String alias, long namespace, int domain)
385             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
386         KeyDescriptor descriptor = new KeyDescriptor();
387         descriptor.nspace = namespace;
388         descriptor.domain = domain;
389         descriptor.alias = alias;
390         descriptor.blob = null;
391 
392         final AndroidKeyStoreKey key = loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor);
393         if (key instanceof AndroidKeyStorePublicKey) {
394             return ((AndroidKeyStorePublicKey) key).getPrivateKey();
395         } else {
396             return key;
397         }
398     }
399 
loadAndroidKeyStoreKeyFromKeystore( @onNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)400     private static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
401             @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)
402             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
403         KeyEntryResponse response = null;
404         try {
405             response = keyStore.getKeyEntry(descriptor);
406         } catch (android.security.KeyStoreException e) {
407             switch (e.getErrorCode()) {
408                 case ResponseCode.KEY_NOT_FOUND:
409                     return null;
410                 case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
411                     throw new KeyPermanentlyInvalidatedException(
412                             "User changed or deleted their auth credentials",
413                             e);
414                 default:
415                     throw (UnrecoverableKeyException)
416                             new UnrecoverableKeyException("Failed to obtain information about key")
417                                     .initCause(e);
418             }
419         }
420 
421         if (response.iSecurityLevel == null) {
422             // This seems to be a pure certificate entry, nothing to return here.
423             return null;
424         }
425 
426         Integer keymasterAlgorithm = null;
427         // We just need one digest for the algorithm name
428         int keymasterDigest = -1;
429         for (Authorization a : response.metadata.authorizations) {
430             switch (a.keyParameter.tag) {
431                 case KeymasterDefs.KM_TAG_ALGORITHM:
432                     keymasterAlgorithm = a.keyParameter.value.getAlgorithm();
433                     break;
434                 case KeymasterDefs.KM_TAG_DIGEST:
435                     if (keymasterDigest == -1) keymasterDigest = a.keyParameter.value.getDigest();
436                     break;
437             }
438         }
439         if (keymasterAlgorithm == null) {
440             throw new UnrecoverableKeyException("Key algorithm unknown");
441         }
442 
443         if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC ||
444                 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES ||
445                 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) {
446             return makeAndroidKeyStoreSecretKeyFromKeyEntryResponse(descriptor, response,
447                     keymasterAlgorithm, keymasterDigest);
448         } else if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
449                 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
450             return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata,
451                     new KeyStoreSecurityLevel(response.iSecurityLevel),
452                     keymasterAlgorithm);
453         } else {
454             throw new UnrecoverableKeyException("Key algorithm unknown");
455         }
456     }
457 }
458