Home | History | Annotate | Download | only in keystore
      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 
     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      */
    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 
    138     private void putSecretKeyFactoryImpl(String algorithm) {
    139         put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreSecretKeyFactorySpi");
    140     }
    141 
    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      */
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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