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.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