• 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 
AndroidKeyStoreProvider()67     public AndroidKeyStoreProvider() {
68         super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");
69 
70         // java.security.KeyStore
71         put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi");
72 
73         // java.security.KeyPairGenerator
74         put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
75         put("KeyPairGenerator.RSA", PACKAGE_NAME +  ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
76 
77         // java.security.KeyFactory
78         putKeyFactoryImpl("EC");
79         putKeyFactoryImpl("RSA");
80 
81         // javax.crypto.KeyGenerator
82         put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
83         put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1");
84         put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA224");
85         put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA256");
86         put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA384");
87         put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA512");
88 
89         // java.security.SecretKeyFactory
90         putSecretKeyFactoryImpl("AES");
91         putSecretKeyFactoryImpl("HmacSHA1");
92         putSecretKeyFactoryImpl("HmacSHA224");
93         putSecretKeyFactoryImpl("HmacSHA256");
94         putSecretKeyFactoryImpl("HmacSHA384");
95         putSecretKeyFactoryImpl("HmacSHA512");
96     }
97 
98     /**
99      * Installs a new instance of this provider (and the
100      * {@link AndroidKeyStoreBCWorkaroundProvider}).
101      */
install()102     public static void install() {
103         Provider[] providers = Security.getProviders();
104         int bcProviderIndex = -1;
105         for (int i = 0; i < providers.length; i++) {
106             Provider provider = providers[i];
107             if ("BC".equals(provider.getName())) {
108                 bcProviderIndex = i;
109                 break;
110             }
111         }
112 
113         Security.addProvider(new AndroidKeyStoreProvider());
114         Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider();
115         if (bcProviderIndex != -1) {
116             // Bouncy Castle provider found -- install the workaround provider above it.
117             // insertProviderAt uses 1-based positions.
118             Security.insertProviderAt(workaroundProvider, bcProviderIndex + 1);
119         } else {
120             // Bouncy Castle provider not found -- install the workaround provider at lowest
121             // priority.
122             Security.addProvider(workaroundProvider);
123         }
124     }
125 
putSecretKeyFactoryImpl(String algorithm)126     private void putSecretKeyFactoryImpl(String algorithm) {
127         put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreSecretKeyFactorySpi");
128     }
129 
putKeyFactoryImpl(String algorithm)130     private void putKeyFactoryImpl(String algorithm) {
131         put("KeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreKeyFactorySpi");
132     }
133 
134     /**
135      * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
136      * primitive.
137      *
138      * <p>The following primitives are supported: {@link Cipher} and {@link Mac}.
139      *
140      * @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
141      *         is not in progress.
142      *
143      * @throws IllegalArgumentException if the provided primitive is not supported or is not backed
144      *         by AndroidKeyStore provider.
145      * @throws IllegalStateException if the provided primitive is not initialized.
146      */
getKeyStoreOperationHandle(Object cryptoPrimitive)147     public static long getKeyStoreOperationHandle(Object cryptoPrimitive) {
148         if (cryptoPrimitive == null) {
149             throw new NullPointerException();
150         }
151         Object spi;
152         if (cryptoPrimitive instanceof Signature) {
153             spi = ((Signature) cryptoPrimitive).getCurrentSpi();
154         } else if (cryptoPrimitive instanceof Mac) {
155             spi = ((Mac) cryptoPrimitive).getCurrentSpi();
156         } else if (cryptoPrimitive instanceof Cipher) {
157             spi = ((Cipher) cryptoPrimitive).getCurrentSpi();
158         } else {
159             throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive
160                     + ". Supported: Signature, Mac, Cipher");
161         }
162         if (spi == null) {
163             throw new IllegalStateException("Crypto primitive not initialized");
164         } else if (!(spi instanceof KeyStoreCryptoOperation)) {
165             throw new IllegalArgumentException(
166                     "Crypto primitive not backed by AndroidKeyStore provider: " + cryptoPrimitive
167                     + ", spi: " + spi);
168         }
169         return ((KeyStoreCryptoOperation) spi).getOperationHandle();
170     }
171 
172     @NonNull
getAndroidKeyStorePublicKey( @onNull String alias, int uid, @NonNull @KeyProperties.KeyAlgorithmEnum String keyAlgorithm, @NonNull byte[] x509EncodedForm)173     public static AndroidKeyStorePublicKey getAndroidKeyStorePublicKey(
174             @NonNull String alias,
175             int uid,
176             @NonNull @KeyProperties.KeyAlgorithmEnum String keyAlgorithm,
177             @NonNull byte[] x509EncodedForm) {
178         PublicKey publicKey;
179         try {
180             KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
181             publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedForm));
182         } catch (NoSuchAlgorithmException e) {
183             throw new ProviderException(
184                     "Failed to obtain " + keyAlgorithm + " KeyFactory", e);
185         } catch (InvalidKeySpecException e) {
186             throw new ProviderException("Invalid X.509 encoding of public key", e);
187         }
188         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
189             return new AndroidKeyStoreECPublicKey(alias, uid, (ECPublicKey) publicKey);
190         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
191             return new AndroidKeyStoreRSAPublicKey(alias, uid, (RSAPublicKey) publicKey);
192         } else {
193             throw new ProviderException("Unsupported Android Keystore public key algorithm: "
194                     + keyAlgorithm);
195         }
196     }
197 
198     @NonNull
getAndroidKeyStorePrivateKey( @onNull AndroidKeyStorePublicKey publicKey)199     public static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey(
200             @NonNull AndroidKeyStorePublicKey publicKey) {
201         String keyAlgorithm = publicKey.getAlgorithm();
202         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
203             return new AndroidKeyStoreECPrivateKey(
204                     publicKey.getAlias(), publicKey.getUid(), ((ECKey) publicKey).getParams());
205         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
206             return new AndroidKeyStoreRSAPrivateKey(
207                     publicKey.getAlias(), publicKey.getUid(), ((RSAKey) publicKey).getModulus());
208         } else {
209             throw new ProviderException("Unsupported Android Keystore public key algorithm: "
210                     + keyAlgorithm);
211         }
212     }
213 
214     @NonNull
loadAndroidKeyStorePublicKeyFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)215     public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
216             @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
217             throws UnrecoverableKeyException {
218         KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
219         int errorCode = keyStore.getKeyCharacteristics(
220                 privateKeyAlias, null, null, uid, keyCharacteristics);
221         if (errorCode != KeyStore.NO_ERROR) {
222             throw (UnrecoverableKeyException)
223                     new UnrecoverableKeyException("Failed to obtain information about private key")
224                     .initCause(KeyStore.getKeyStoreException(errorCode));
225         }
226         ExportResult exportResult = keyStore.exportKey(
227                 privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null, uid);
228         if (exportResult.resultCode != KeyStore.NO_ERROR) {
229             throw (UnrecoverableKeyException)
230                     new UnrecoverableKeyException("Failed to obtain X.509 form of public key")
231                     .initCause(KeyStore.getKeyStoreException(exportResult.resultCode));
232         }
233         final byte[] x509EncodedPublicKey = exportResult.exportData;
234 
235         Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
236         if (keymasterAlgorithm == null) {
237             throw new UnrecoverableKeyException("Key algorithm unknown");
238         }
239 
240         String jcaKeyAlgorithm;
241         try {
242             jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(
243                     keymasterAlgorithm);
244         } catch (IllegalArgumentException e) {
245             throw (UnrecoverableKeyException)
246                     new UnrecoverableKeyException("Failed to load private key")
247                     .initCause(e);
248         }
249 
250         return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
251                 privateKeyAlias, uid, jcaKeyAlgorithm, x509EncodedPublicKey);
252     }
253 
254     @NonNull
loadAndroidKeyStoreKeyPairFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)255     public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
256             @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
257             throws UnrecoverableKeyException {
258         AndroidKeyStorePublicKey publicKey =
259                 loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid);
260         AndroidKeyStorePrivateKey privateKey =
261                 AndroidKeyStoreProvider.getAndroidKeyStorePrivateKey(publicKey);
262         return new KeyPair(publicKey, privateKey);
263     }
264 
265     @NonNull
loadAndroidKeyStorePrivateKeyFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)266     public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
267             @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
268             throws UnrecoverableKeyException {
269         KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid);
270         return (AndroidKeyStorePrivateKey) keyPair.getPrivate();
271     }
272 
273     @NonNull
loadAndroidKeyStoreSecretKeyFromKeystore( @onNull KeyStore keyStore, @NonNull String secretKeyAlias, int uid)274     public static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
275             @NonNull KeyStore keyStore, @NonNull String secretKeyAlias, int uid)
276             throws UnrecoverableKeyException {
277         KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
278         int errorCode = keyStore.getKeyCharacteristics(
279                 secretKeyAlias, null, null, uid, keyCharacteristics);
280         if (errorCode != KeyStore.NO_ERROR) {
281             throw (UnrecoverableKeyException)
282                     new UnrecoverableKeyException("Failed to obtain information about key")
283                             .initCause(KeyStore.getKeyStoreException(errorCode));
284         }
285 
286         Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
287         if (keymasterAlgorithm == null) {
288             throw new UnrecoverableKeyException("Key algorithm unknown");
289         }
290 
291         List<Integer> keymasterDigests = keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST);
292         int keymasterDigest;
293         if (keymasterDigests.isEmpty()) {
294             keymasterDigest = -1;
295         } else {
296             // More than one digest can be permitted for this key. Use the first one to form the
297             // JCA key algorithm name.
298             keymasterDigest = keymasterDigests.get(0);
299         }
300 
301         @KeyProperties.KeyAlgorithmEnum String keyAlgorithmString;
302         try {
303             keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
304                     keymasterAlgorithm, keymasterDigest);
305         } catch (IllegalArgumentException e) {
306             throw (UnrecoverableKeyException)
307                     new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
308         }
309 
310         return new AndroidKeyStoreSecretKey(secretKeyAlias, uid, keyAlgorithmString);
311     }
312 
313     /**
314      * Returns an {@code AndroidKeyStore} {@link java.security.KeyStore}} of the specified UID.
315      * The {@code KeyStore} contains keys and certificates owned by that UID. Such cross-UID
316      * access is permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN)
317      * all of which are system.
318      *
319      * <p>Note: the returned {@code KeyStore} is already initialized/loaded. Thus, there is
320      * no need to invoke {@code load} on it.
321      */
322     @NonNull
getKeyStoreForUid(int uid)323     public static java.security.KeyStore getKeyStoreForUid(int uid)
324             throws KeyStoreException, NoSuchProviderException {
325         java.security.KeyStore result =
326                 java.security.KeyStore.getInstance("AndroidKeyStore", PROVIDER_NAME);
327         try {
328             result.load(new AndroidKeyStoreLoadStoreParameter(uid));
329         } catch (NoSuchAlgorithmException | CertificateException | IOException e) {
330             throw new KeyStoreException(
331                     "Failed to load AndroidKeyStore KeyStore for UID " + uid, e);
332         }
333         return result;
334     }
335 }
336