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