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