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