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     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      */
    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 
    126     private void putSecretKeyFactoryImpl(String algorithm) {
    127         put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreSecretKeyFactorySpi");
    128     }
    129 
    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      */
    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
    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
    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
    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
    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
    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
    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
    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