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