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