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