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