• 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.keystore;
18 
19 import android.annotation.NonNull;
20 import android.annotation.SystemApi;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.security.KeyStore;
23 import android.security.keymaster.ExportResult;
24 import android.security.keymaster.KeyCharacteristics;
25 import android.security.keymaster.KeymasterDefs;
26 
27 import java.io.IOException;
28 import java.security.KeyFactory;
29 import java.security.KeyPair;
30 import java.security.KeyStoreException;
31 import java.security.NoSuchAlgorithmException;
32 import java.security.NoSuchProviderException;
33 import java.security.Provider;
34 import java.security.ProviderException;
35 import java.security.PublicKey;
36 import java.security.Security;
37 import java.security.Signature;
38 import java.security.UnrecoverableKeyException;
39 import java.security.cert.CertificateException;
40 import java.security.interfaces.ECKey;
41 import java.security.interfaces.ECPublicKey;
42 import java.security.interfaces.RSAKey;
43 import java.security.interfaces.RSAPublicKey;
44 import java.security.spec.InvalidKeySpecException;
45 import java.security.spec.X509EncodedKeySpec;
46 import java.util.List;
47 
48 import javax.crypto.Cipher;
49 import javax.crypto.Mac;
50 
51 /**
52  * A provider focused on providing JCA interfaces for the Android KeyStore.
53  *
54  * @hide
55  */
56 @SystemApi
57 public class AndroidKeyStoreProvider extends Provider {
58     private static final String PROVIDER_NAME = "AndroidKeyStore";
59 
60     // IMPLEMENTATION NOTE: Class names are hard-coded in this provider to avoid loading these
61     // classes when this provider is instantiated and installed early on during each app's
62     // initialization process.
63     //
64     // Crypto operations operating on the AndroidKeyStore keys must not be offered by this provider.
65     // Instead, they need to be offered by AndroidKeyStoreBCWorkaroundProvider. See its Javadoc
66     // for details.
67 
68     private static final String PACKAGE_NAME = "android.security.keystore";
69 
70     private static final String DESEDE_SYSTEM_PROPERTY =
71             "ro.hardware.keystore_desede";
72 
73     /** @hide **/
AndroidKeyStoreProvider()74     public AndroidKeyStoreProvider() {
75         super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");
76 
77         boolean supports3DES = "true".equals(android.os.SystemProperties.get(DESEDE_SYSTEM_PROPERTY));
78 
79         // java.security.KeyStore
80         put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi");
81 
82         // java.security.KeyPairGenerator
83         put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
84         put("KeyPairGenerator.RSA", PACKAGE_NAME +  ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
85 
86         // java.security.KeyFactory
87         putKeyFactoryImpl("EC");
88         putKeyFactoryImpl("RSA");
89 
90         // javax.crypto.KeyGenerator
91         put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
92         put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1");
93         put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA224");
94         put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA256");
95         put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA384");
96         put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA512");
97 
98         if (supports3DES) {
99             put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede");
100         }
101 
102         // java.security.SecretKeyFactory
103         putSecretKeyFactoryImpl("AES");
104         if (supports3DES) {
105             putSecretKeyFactoryImpl("DESede");
106         }
107         putSecretKeyFactoryImpl("HmacSHA1");
108         putSecretKeyFactoryImpl("HmacSHA224");
109         putSecretKeyFactoryImpl("HmacSHA256");
110         putSecretKeyFactoryImpl("HmacSHA384");
111         putSecretKeyFactoryImpl("HmacSHA512");
112     }
113 
114     /**
115      * Installs a new instance of this provider (and the
116      * {@link AndroidKeyStoreBCWorkaroundProvider}).
117      * @hide
118      */
install()119     public static void install() {
120         Provider[] providers = Security.getProviders();
121         int bcProviderIndex = -1;
122         for (int i = 0; i < providers.length; i++) {
123             Provider provider = providers[i];
124             if ("BC".equals(provider.getName())) {
125                 bcProviderIndex = i;
126                 break;
127             }
128         }
129 
130         Security.addProvider(new AndroidKeyStoreProvider());
131         Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider();
132         if (bcProviderIndex != -1) {
133             // Bouncy Castle provider found -- install the workaround provider above it.
134             // insertProviderAt uses 1-based positions.
135             Security.insertProviderAt(workaroundProvider, bcProviderIndex + 1);
136         } else {
137             // Bouncy Castle provider not found -- install the workaround provider at lowest
138             // priority.
139             Security.addProvider(workaroundProvider);
140         }
141     }
142 
putSecretKeyFactoryImpl(String algorithm)143     private void putSecretKeyFactoryImpl(String algorithm) {
144         put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreSecretKeyFactorySpi");
145     }
146 
putKeyFactoryImpl(String algorithm)147     private void putKeyFactoryImpl(String algorithm) {
148         put("KeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreKeyFactorySpi");
149     }
150 
151     /**
152      * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
153      * primitive.
154      *
155      * <p>The following primitives are supported: {@link Cipher} and {@link Mac}.
156      *
157      * @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
158      *         is not in progress.
159      *
160      * @throws IllegalArgumentException if the provided primitive is not supported or is not backed
161      *         by AndroidKeyStore provider.
162      * @throws IllegalStateException if the provided primitive is not initialized.
163      * @hide
164      */
165     @UnsupportedAppUsage
getKeyStoreOperationHandle(Object cryptoPrimitive)166     public static long getKeyStoreOperationHandle(Object cryptoPrimitive) {
167         if (cryptoPrimitive == null) {
168             throw new NullPointerException();
169         }
170         Object spi;
171         if (cryptoPrimitive instanceof Signature) {
172             spi = ((Signature) cryptoPrimitive).getCurrentSpi();
173         } else if (cryptoPrimitive instanceof Mac) {
174             spi = ((Mac) cryptoPrimitive).getCurrentSpi();
175         } else if (cryptoPrimitive instanceof Cipher) {
176             spi = ((Cipher) cryptoPrimitive).getCurrentSpi();
177         } else {
178             throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive
179                     + ". Supported: Signature, Mac, Cipher");
180         }
181         if (spi == null) {
182             throw new IllegalStateException("Crypto primitive not initialized");
183         } else if (!(spi instanceof KeyStoreCryptoOperation)) {
184             throw new IllegalArgumentException(
185                     "Crypto primitive not backed by AndroidKeyStore provider: " + cryptoPrimitive
186                     + ", spi: " + spi);
187         }
188         return ((KeyStoreCryptoOperation) spi).getOperationHandle();
189     }
190 
191     /** @hide **/
192     @NonNull
getAndroidKeyStorePublicKey( @onNull String alias, int uid, @NonNull @KeyProperties.KeyAlgorithmEnum String keyAlgorithm, @NonNull byte[] x509EncodedForm)193     public static AndroidKeyStorePublicKey getAndroidKeyStorePublicKey(
194             @NonNull String alias,
195             int uid,
196             @NonNull @KeyProperties.KeyAlgorithmEnum String keyAlgorithm,
197             @NonNull byte[] x509EncodedForm) {
198         PublicKey publicKey;
199         try {
200             KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
201             publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedForm));
202         } catch (NoSuchAlgorithmException e) {
203             throw new ProviderException(
204                     "Failed to obtain " + keyAlgorithm + " KeyFactory", e);
205         } catch (InvalidKeySpecException e) {
206             throw new ProviderException("Invalid X.509 encoding of public key", e);
207         }
208         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
209             return new AndroidKeyStoreECPublicKey(alias, uid, (ECPublicKey) publicKey);
210         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
211             return new AndroidKeyStoreRSAPublicKey(alias, uid, (RSAPublicKey) publicKey);
212         } else {
213             throw new ProviderException("Unsupported Android Keystore public key algorithm: "
214                     + keyAlgorithm);
215         }
216     }
217 
218     @NonNull
getAndroidKeyStorePrivateKey( @onNull AndroidKeyStorePublicKey publicKey)219     private static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey(
220             @NonNull AndroidKeyStorePublicKey publicKey) {
221         String keyAlgorithm = publicKey.getAlgorithm();
222         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
223             return new AndroidKeyStoreECPrivateKey(
224                     publicKey.getAlias(), publicKey.getUid(), ((ECKey) publicKey).getParams());
225         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
226             return new AndroidKeyStoreRSAPrivateKey(
227                     publicKey.getAlias(), publicKey.getUid(), ((RSAKey) publicKey).getModulus());
228         } else {
229             throw new ProviderException("Unsupported Android Keystore public key algorithm: "
230                     + keyAlgorithm);
231         }
232     }
233 
234     @NonNull
getKeyCharacteristics(@onNull KeyStore keyStore, @NonNull String alias, int uid)235     private static KeyCharacteristics getKeyCharacteristics(@NonNull KeyStore keyStore,
236             @NonNull String alias, int uid)
237             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
238         KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
239         int errorCode = keyStore.getKeyCharacteristics(
240                 alias, null, null, uid, keyCharacteristics);
241         if (errorCode == KeyStore.KEY_PERMANENTLY_INVALIDATED) {
242             throw (KeyPermanentlyInvalidatedException)
243                 new KeyPermanentlyInvalidatedException(
244                             "User changed or deleted their auth credentials",
245                             KeyStore.getKeyStoreException(errorCode));
246         }
247         if (errorCode != KeyStore.NO_ERROR) {
248             throw (UnrecoverableKeyException)
249                     new UnrecoverableKeyException("Failed to obtain information about key")
250                             .initCause(KeyStore.getKeyStoreException(errorCode));
251         }
252         return keyCharacteristics;
253     }
254 
255     @NonNull
loadAndroidKeyStorePublicKeyFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, KeyCharacteristics keyCharacteristics)256     private static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
257             @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
258             KeyCharacteristics keyCharacteristics)
259             throws UnrecoverableKeyException {
260         ExportResult exportResult = keyStore.exportKey(
261                 privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null, uid);
262         if (exportResult.resultCode != KeyStore.NO_ERROR) {
263             throw (UnrecoverableKeyException)
264                     new UnrecoverableKeyException("Failed to obtain X.509 form of public key")
265                     .initCause(KeyStore.getKeyStoreException(exportResult.resultCode));
266         }
267         final byte[] x509EncodedPublicKey = exportResult.exportData;
268 
269         Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
270         if (keymasterAlgorithm == null) {
271             throw new UnrecoverableKeyException("Key algorithm unknown");
272         }
273 
274         String jcaKeyAlgorithm;
275         try {
276             jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(
277                     keymasterAlgorithm);
278         } catch (IllegalArgumentException e) {
279             throw (UnrecoverableKeyException)
280                     new UnrecoverableKeyException("Failed to load private key")
281                     .initCause(e);
282         }
283 
284         return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
285                 privateKeyAlias, uid, jcaKeyAlgorithm, x509EncodedPublicKey);
286     }
287 
288     /** @hide **/
289     @NonNull
loadAndroidKeyStorePublicKeyFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)290     public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
291             @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
292             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
293         return loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid,
294                 getKeyCharacteristics(keyStore, privateKeyAlias, uid));
295     }
296 
297     @NonNull
loadAndroidKeyStoreKeyPairFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics)298     private static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
299             @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
300             @NonNull KeyCharacteristics keyCharacteristics)
301             throws UnrecoverableKeyException {
302         AndroidKeyStorePublicKey publicKey =
303                 loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid,
304                         keyCharacteristics);
305         AndroidKeyStorePrivateKey privateKey =
306                 AndroidKeyStoreProvider.getAndroidKeyStorePrivateKey(publicKey);
307         return new KeyPair(publicKey, privateKey);
308     }
309 
310     /** @hide **/
311     @NonNull
loadAndroidKeyStoreKeyPairFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)312     public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
313             @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
314             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
315         return loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid,
316                 getKeyCharacteristics(keyStore, privateKeyAlias, uid));
317     }
318 
319     @NonNull
loadAndroidKeyStorePrivateKeyFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics)320     private static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
321             @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
322             @NonNull KeyCharacteristics keyCharacteristics)
323             throws UnrecoverableKeyException {
324         KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid,
325                 keyCharacteristics);
326         return (AndroidKeyStorePrivateKey) keyPair.getPrivate();
327     }
328 
329     /** @hide **/
330     @NonNull
loadAndroidKeyStorePrivateKeyFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)331     public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
332             @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
333             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
334         return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, privateKeyAlias, uid,
335                 getKeyCharacteristics(keyStore, privateKeyAlias, uid));
336     }
337 
338     @NonNull
loadAndroidKeyStoreSecretKeyFromKeystore( @onNull String secretKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics)339     private static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
340             @NonNull String secretKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics)
341             throws UnrecoverableKeyException {
342         Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
343         if (keymasterAlgorithm == null) {
344             throw new UnrecoverableKeyException("Key algorithm unknown");
345         }
346 
347         List<Integer> keymasterDigests = keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST);
348         int keymasterDigest;
349         if (keymasterDigests.isEmpty()) {
350             keymasterDigest = -1;
351         } else {
352             // More than one digest can be permitted for this key. Use the first one to form the
353             // JCA key algorithm name.
354             keymasterDigest = keymasterDigests.get(0);
355         }
356 
357         @KeyProperties.KeyAlgorithmEnum String keyAlgorithmString;
358         try {
359             keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
360                     keymasterAlgorithm, keymasterDigest);
361         } catch (IllegalArgumentException e) {
362             throw (UnrecoverableKeyException)
363                     new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
364         }
365 
366         return new AndroidKeyStoreSecretKey(secretKeyAlias, uid, keyAlgorithmString);
367     }
368 
369     /** @hide **/
370     @NonNull
loadAndroidKeyStoreKeyFromKeystore( @onNull KeyStore keyStore, @NonNull String userKeyAlias, int uid)371     public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
372             @NonNull KeyStore keyStore, @NonNull String userKeyAlias, int uid)
373             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException  {
374         KeyCharacteristics keyCharacteristics = getKeyCharacteristics(keyStore, userKeyAlias, uid);
375 
376         Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
377         if (keymasterAlgorithm == null) {
378             throw new UnrecoverableKeyException("Key algorithm unknown");
379         }
380 
381         if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC ||
382                 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES ||
383                 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) {
384             return loadAndroidKeyStoreSecretKeyFromKeystore(userKeyAlias, uid,
385                     keyCharacteristics);
386         } else if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
387                 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
388             return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, userKeyAlias, uid,
389                     keyCharacteristics);
390         } else {
391             throw new UnrecoverableKeyException("Key algorithm unknown");
392         }
393     }
394 
395     /**
396      * Returns an {@code AndroidKeyStore} {@link java.security.KeyStore}} of the specified UID.
397      * The {@code KeyStore} contains keys and certificates owned by that UID. Such cross-UID
398      * access is permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN)
399      * all of which are system.
400      *
401      * <p>Note: the returned {@code KeyStore} is already initialized/loaded. Thus, there is
402      * no need to invoke {@code load} on it.
403      *
404      * @param uid Uid for which the keystore provider is requested.
405      * @throws KeyStoreException if a KeyStoreSpi implementation for the specified type is not
406      * available from the specified provider.
407      * @throws NoSuchProviderException If the specified provider is not registered in the security
408      * provider list.
409      * @hide
410      */
411     @SystemApi
412     @NonNull
getKeyStoreForUid(int uid)413     public static java.security.KeyStore getKeyStoreForUid(int uid)
414             throws KeyStoreException, NoSuchProviderException {
415         java.security.KeyStore result =
416                 java.security.KeyStore.getInstance("AndroidKeyStore", PROVIDER_NAME);
417         try {
418             result.load(new AndroidKeyStoreLoadStoreParameter(uid));
419         } catch (NoSuchAlgorithmException | CertificateException | IOException e) {
420             throw new KeyStoreException(
421                     "Failed to load AndroidKeyStore KeyStore for UID " + uid, e);
422         }
423         return result;
424     }
425 }
426