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