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.keystore2; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.security.KeyStore2; 22 import android.security.KeyStoreSecurityLevel; 23 import android.security.keymaster.KeymasterDefs; 24 import android.security.keystore.KeyPermanentlyInvalidatedException; 25 import android.security.keystore.KeyProperties; 26 import android.security.keystore.KeyStoreCryptoOperation; 27 import android.system.keystore2.Authorization; 28 import android.system.keystore2.Domain; 29 import android.system.keystore2.KeyDescriptor; 30 import android.system.keystore2.KeyEntryResponse; 31 import android.system.keystore2.KeyMetadata; 32 import android.system.keystore2.ResponseCode; 33 34 import java.security.KeyPair; 35 import java.security.Provider; 36 import java.security.ProviderException; 37 import java.security.PublicKey; 38 import java.security.Security; 39 import java.security.Signature; 40 import java.security.UnrecoverableKeyException; 41 import java.security.cert.X509Certificate; 42 import java.security.interfaces.ECPublicKey; 43 import java.security.interfaces.RSAPublicKey; 44 45 import javax.crypto.Cipher; 46 import javax.crypto.KeyAgreement; 47 import javax.crypto.Mac; 48 import javax.crypto.SecretKey; 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 private 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.keystore2"; 67 68 private static final String DESEDE_SYSTEM_PROPERTY = 69 "ro.hardware.keystore_desede"; 70 71 // Conscrypt returns the Ed25519 OID as the JCA key algorithm. 72 private static final String ED25519_OID = "1.3.101.112"; 73 // Conscrypt returns "XDH" as the X25519 JCA key algorithm. 74 private static final String X25519_ALIAS = "XDH"; 75 76 /** @hide **/ AndroidKeyStoreProvider()77 public AndroidKeyStoreProvider() { 78 super(PROVIDER_NAME, 1.0, "Android KeyStore security provider"); 79 80 boolean supports3DES = "true".equals(android.os.SystemProperties.get(DESEDE_SYSTEM_PROPERTY)); 81 82 // java.security.KeyStore 83 put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi"); 84 85 // java.security.KeyPairGenerator 86 put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC"); 87 put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA"); 88 put("KeyPairGenerator.XDH", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$XDH"); 89 put("KeyPairGenerator.ED25519", PACKAGE_NAME 90 + ".AndroidKeyStoreKeyPairGeneratorSpi$ED25519"); 91 92 // java.security.KeyFactory 93 putKeyFactoryImpl("EC"); 94 putKeyFactoryImpl("RSA"); 95 putKeyFactoryImpl("XDH"); 96 putKeyFactoryImpl("ED25519"); 97 98 // javax.crypto.KeyGenerator 99 put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES"); 100 put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1"); 101 put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA224"); 102 put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA256"); 103 put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA384"); 104 put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA512"); 105 106 if (supports3DES) { 107 put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede"); 108 } 109 110 // javax.crypto.KeyAgreement 111 put("KeyAgreement.ECDH", PACKAGE_NAME + ".AndroidKeyStoreKeyAgreementSpi$ECDH"); 112 put("KeyAgreement.XDH", PACKAGE_NAME + ".AndroidKeyStoreKeyAgreementSpi$XDH"); 113 114 // java.security.SecretKeyFactory 115 putSecretKeyFactoryImpl("AES"); 116 if (supports3DES) { 117 putSecretKeyFactoryImpl("DESede"); 118 } 119 putSecretKeyFactoryImpl("HmacSHA1"); 120 putSecretKeyFactoryImpl("HmacSHA224"); 121 putSecretKeyFactoryImpl("HmacSHA256"); 122 putSecretKeyFactoryImpl("HmacSHA384"); 123 putSecretKeyFactoryImpl("HmacSHA512"); 124 } 125 126 /** 127 * Installs a new instance of this provider (and the 128 * {@link AndroidKeyStoreBCWorkaroundProvider}). 129 * @hide 130 */ install()131 public static void install() { 132 Provider[] providers = Security.getProviders(); 133 int bcProviderIndex = -1; 134 for (int i = 0; i < providers.length; i++) { 135 Provider provider = providers[i]; 136 if ("BC".equals(provider.getName())) { 137 bcProviderIndex = i; 138 break; 139 } 140 } 141 142 Security.addProvider(new AndroidKeyStoreProvider()); 143 Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider(); 144 if (bcProviderIndex != -1) { 145 // Bouncy Castle provider found -- install the workaround provider above it. 146 // insertProviderAt uses 1-based positions. 147 Security.insertProviderAt(workaroundProvider, bcProviderIndex + 1); 148 } else { 149 // Bouncy Castle provider not found -- install the workaround provider at lowest 150 // priority. 151 Security.addProvider(workaroundProvider); 152 } 153 } 154 putSecretKeyFactoryImpl(String algorithm)155 private void putSecretKeyFactoryImpl(String algorithm) { 156 put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreSecretKeyFactorySpi"); 157 } 158 putKeyFactoryImpl(String algorithm)159 private void putKeyFactoryImpl(String algorithm) { 160 put("KeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreKeyFactorySpi"); 161 } 162 163 /** 164 * Gets the Android KeyStore operation handle corresponding to the provided JCA crypto 165 * primitive. 166 * 167 * <p>The following primitives are supported: {@link Cipher}, {@link Signature} and {@link Mac}. 168 * 169 * @return Android KeyStore operation handle or {@code 0} if the provided primitive's Android 170 * KeyStore operation is not in progress. 171 * 172 * @throws IllegalArgumentException if the provided primitive is not supported or is not backed 173 * by AndroidKeyStore provider. 174 * @throws IllegalStateException if the provided primitive is not initialized. 175 * @hide 176 */ getKeyStoreOperationHandle(Object cryptoPrimitive)177 public static long getKeyStoreOperationHandle(Object cryptoPrimitive) { 178 if (cryptoPrimitive == null) { 179 throw new NullPointerException(); 180 } 181 Object spi; 182 if (cryptoPrimitive instanceof Signature) { 183 spi = ((Signature) cryptoPrimitive).getCurrentSpi(); 184 } else if (cryptoPrimitive instanceof Mac) { 185 spi = ((Mac) cryptoPrimitive).getCurrentSpi(); 186 } else if (cryptoPrimitive instanceof Cipher) { 187 spi = ((Cipher) cryptoPrimitive).getCurrentSpi(); 188 } else if (cryptoPrimitive instanceof KeyAgreement) { 189 spi = ((KeyAgreement) cryptoPrimitive).getCurrentSpi(); 190 } else { 191 throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive 192 + ". Supported: Signature, Mac, Cipher"); 193 } 194 if (spi == null) { 195 throw new IllegalStateException("Crypto primitive not initialized"); 196 } else if (!(spi instanceof KeyStoreCryptoOperation)) { 197 throw new IllegalArgumentException( 198 "Crypto primitive not backed by AndroidKeyStore provider: " + cryptoPrimitive 199 + ", spi: " + spi); 200 } 201 return ((KeyStoreCryptoOperation) spi).getOperationHandle(); 202 } 203 204 /** 205 * This helper function gets called if the key loaded from the keystore daemon 206 * is for an asymmetric algorithm. It constructs an instance of {@link AndroidKeyStorePublicKey} 207 * which implements {@link PublicKey}. 208 * 209 * @param descriptor The original key descriptor that was used to load the key. 210 * 211 * @param metadata The key metadata which includes the public key material, a reference to the 212 * stored private key material, the key characteristics. 213 * @param iSecurityLevel A binder interface that allows using the private key. 214 * @param algorithm Must indicate EC or RSA. 215 * @return AndroidKeyStorePublicKey 216 * @throws UnrecoverableKeyException 217 * @hide 218 */ 219 @NonNull makeAndroidKeyStorePublicKeyFromKeyEntryResponse( @onNull KeyDescriptor descriptor, @NonNull KeyMetadata metadata, @NonNull KeyStoreSecurityLevel iSecurityLevel, int algorithm)220 static AndroidKeyStorePublicKey makeAndroidKeyStorePublicKeyFromKeyEntryResponse( 221 @NonNull KeyDescriptor descriptor, 222 @NonNull KeyMetadata metadata, 223 @NonNull KeyStoreSecurityLevel iSecurityLevel, int algorithm) 224 throws UnrecoverableKeyException { 225 if (metadata.certificate == null) { 226 throw new UnrecoverableKeyException("Failed to obtain X.509 form of public key." 227 + " Keystore has no public certificate stored."); 228 } 229 final byte[] x509PublicCert = metadata.certificate; 230 231 final X509Certificate parsedX509Certificate = 232 AndroidKeyStoreSpi.toCertificate(x509PublicCert); 233 if (parsedX509Certificate == null) { 234 throw new UnrecoverableKeyException("Failed to parse the X.509 certificate containing" 235 + " the public key. This likely indicates a hardware problem."); 236 } 237 238 PublicKey publicKey = parsedX509Certificate.getPublicKey(); 239 240 String jcaKeyAlgorithm = publicKey.getAlgorithm(); 241 242 if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) { 243 return new AndroidKeyStoreECPublicKey(descriptor, metadata, 244 iSecurityLevel, (ECPublicKey) publicKey); 245 } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(jcaKeyAlgorithm)) { 246 return new AndroidKeyStoreRSAPublicKey(descriptor, metadata, 247 iSecurityLevel, (RSAPublicKey) publicKey); 248 } else if (ED25519_OID.equalsIgnoreCase(jcaKeyAlgorithm)) { 249 final byte[] publicKeyEncoded = publicKey.getEncoded(); 250 return new AndroidKeyStoreEdECPublicKey(descriptor, metadata, ED25519_OID, 251 iSecurityLevel, publicKeyEncoded); 252 } else if (X25519_ALIAS.equalsIgnoreCase(jcaKeyAlgorithm)) { 253 return new AndroidKeyStoreXDHPublicKey(descriptor, metadata, X25519_ALIAS, 254 iSecurityLevel, publicKey.getEncoded()); 255 } else { 256 throw new ProviderException("Unsupported Android Keystore public key algorithm: " 257 + jcaKeyAlgorithm); 258 } 259 } 260 261 /** @hide **/ 262 @NonNull loadAndroidKeyStorePublicKeyFromKeystore( @onNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace)263 public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore( 264 @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace) 265 throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { 266 AndroidKeyStoreKey key = 267 loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace); 268 if (key instanceof AndroidKeyStorePublicKey) { 269 return (AndroidKeyStorePublicKey) key; 270 } else { 271 throw new UnrecoverableKeyException("No asymmetric key found by the given alias."); 272 } 273 } 274 275 /** @hide **/ 276 @NonNull loadAndroidKeyStoreKeyPairFromKeystore( @onNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)277 public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore( 278 @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor) 279 throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { 280 AndroidKeyStoreKey key = 281 loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor); 282 if (key instanceof AndroidKeyStorePublicKey) { 283 AndroidKeyStorePublicKey publicKey = (AndroidKeyStorePublicKey) key; 284 return new KeyPair(publicKey, publicKey.getPrivateKey()); 285 } else { 286 throw new UnrecoverableKeyException("No asymmetric key found by the given alias."); 287 } 288 } 289 290 /** @hide **/ 291 @NonNull loadAndroidKeyStorePrivateKeyFromKeystore( @onNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace)292 public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore( 293 @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace) 294 throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { 295 AndroidKeyStoreKey key = 296 loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace); 297 if (key instanceof AndroidKeyStorePublicKey) { 298 return ((AndroidKeyStorePublicKey) key).getPrivateKey(); 299 } else { 300 throw new UnrecoverableKeyException("No asymmetric key found by the given alias."); 301 } 302 } 303 304 /** @hide **/ 305 @NonNull loadAndroidKeyStoreSecretKeyFromKeystore( @onNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)306 public static SecretKey loadAndroidKeyStoreSecretKeyFromKeystore( 307 @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor) 308 throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { 309 310 AndroidKeyStoreKey key = 311 loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor); 312 if (key instanceof SecretKey) { 313 return (SecretKey) key; 314 } else { 315 throw new UnrecoverableKeyException("No secret key found by the given alias."); 316 } 317 } 318 319 @NonNull makeAndroidKeyStoreSecretKeyFromKeyEntryResponse( @onNull KeyDescriptor descriptor, @NonNull KeyEntryResponse response, int algorithm, int digest)320 private static AndroidKeyStoreSecretKey makeAndroidKeyStoreSecretKeyFromKeyEntryResponse( 321 @NonNull KeyDescriptor descriptor, 322 @NonNull KeyEntryResponse response, int algorithm, int digest) 323 throws UnrecoverableKeyException { 324 @KeyProperties.KeyAlgorithmEnum String keyAlgorithmString; 325 try { 326 keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm( 327 algorithm, digest); 328 } catch (IllegalArgumentException e) { 329 throw (UnrecoverableKeyException) 330 new UnrecoverableKeyException("Unsupported secret key type").initCause(e); 331 } 332 333 return new AndroidKeyStoreSecretKey(descriptor, 334 response.metadata, keyAlgorithmString, 335 new KeyStoreSecurityLevel(response.iSecurityLevel)); 336 } 337 338 /** 339 * Loads an AndroidKeyStoreKey from the AndroidKeyStore backend. 340 * 341 * @param keyStore The keystore2 backend. 342 * @param alias The alias of the key in the Keystore database. 343 * @param namespace The Keystore namespace. This is used by system api only to request 344 * Android system specific keystore namespace, which can be configured 345 * in the device's SEPolicy. Third party apps and most system components 346 * set this parameter to -1 to indicate their application specific namespace. 347 * See <a href="https://source.android.com/security/keystore#access-control"> 348 * Keystore 2.0 access control</a> 349 * @hide 350 **/ 351 @NonNull loadAndroidKeyStoreKeyFromKeystore( @onNull KeyStore2 keyStore, @NonNull String alias, int namespace)352 public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore( 353 @NonNull KeyStore2 keyStore, @NonNull String alias, int namespace) 354 throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { 355 int descriptorNamespace; 356 int descriptorDomain; 357 if (namespace == KeyProperties.NAMESPACE_APPLICATION) { 358 descriptorNamespace = KeyProperties.NAMESPACE_APPLICATION; // ignored; 359 descriptorDomain = Domain.APP; 360 } else { 361 descriptorNamespace = namespace; 362 descriptorDomain = Domain.SELINUX; 363 } 364 return loadAndroidKeyStoreKeyFromKeystore(keyStore, alias, descriptorNamespace, 365 descriptorDomain); 366 } 367 368 /** 369 * Loads an AndroidKeyStoreKey from the AndroidKeyStore backend. 370 * 371 * @param keyStore The keystore2 backend 372 * @param alias The alias of the key in the Keystore database 373 * @param namespace The Keystore namespace. This is used by system api only to request 374 * Android system specific keystore namespace, which can be configured 375 * in the device's SEPolicy. Third party apps and most system components 376 * set this parameter to -1 to indicate their application specific namespace. 377 * See <a href="https://source.android.com/security/keystore#access-control"> 378 * Keystore 2.0 access control</a> 379 * @param domain The Keystore domain 380 * @return an AndroidKeyStoreKey corresponding to the provided values for the KeyDescriptor 381 * @hide 382 */ loadAndroidKeyStoreKeyFromKeystore(@onNull KeyStore2 keyStore, @Nullable String alias, long namespace, int domain)383 public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(@NonNull KeyStore2 keyStore, 384 @Nullable String alias, long namespace, int domain) 385 throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { 386 KeyDescriptor descriptor = new KeyDescriptor(); 387 descriptor.nspace = namespace; 388 descriptor.domain = domain; 389 descriptor.alias = alias; 390 descriptor.blob = null; 391 392 final AndroidKeyStoreKey key = loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor); 393 if (key instanceof AndroidKeyStorePublicKey) { 394 return ((AndroidKeyStorePublicKey) key).getPrivateKey(); 395 } else { 396 return key; 397 } 398 } 399 loadAndroidKeyStoreKeyFromKeystore( @onNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)400 private static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore( 401 @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor) 402 throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { 403 KeyEntryResponse response = null; 404 try { 405 response = keyStore.getKeyEntry(descriptor); 406 } catch (android.security.KeyStoreException e) { 407 switch (e.getErrorCode()) { 408 case ResponseCode.KEY_NOT_FOUND: 409 return null; 410 case ResponseCode.KEY_PERMANENTLY_INVALIDATED: 411 throw new KeyPermanentlyInvalidatedException( 412 "User changed or deleted their auth credentials", 413 e); 414 default: 415 throw (UnrecoverableKeyException) 416 new UnrecoverableKeyException("Failed to obtain information about key") 417 .initCause(e); 418 } 419 } 420 421 if (response.iSecurityLevel == null) { 422 // This seems to be a pure certificate entry, nothing to return here. 423 return null; 424 } 425 426 Integer keymasterAlgorithm = null; 427 // We just need one digest for the algorithm name 428 int keymasterDigest = -1; 429 for (Authorization a : response.metadata.authorizations) { 430 switch (a.keyParameter.tag) { 431 case KeymasterDefs.KM_TAG_ALGORITHM: 432 keymasterAlgorithm = a.keyParameter.value.getAlgorithm(); 433 break; 434 case KeymasterDefs.KM_TAG_DIGEST: 435 if (keymasterDigest == -1) keymasterDigest = a.keyParameter.value.getDigest(); 436 break; 437 } 438 } 439 if (keymasterAlgorithm == null) { 440 throw new UnrecoverableKeyException("Key algorithm unknown"); 441 } 442 443 if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC || 444 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES || 445 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) { 446 return makeAndroidKeyStoreSecretKeyFromKeyEntryResponse(descriptor, response, 447 keymasterAlgorithm, keymasterDigest); 448 } else if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA || 449 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) { 450 return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata, 451 new KeyStoreSecurityLevel(response.iSecurityLevel), 452 keymasterAlgorithm); 453 } else { 454 throw new UnrecoverableKeyException("Key algorithm unknown"); 455 } 456 } 457 } 458