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