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.SystemApi; 21 import android.compat.annotation.UnsupportedAppUsage; 22 import android.security.KeyStore; 23 import android.security.keymaster.ExportResult; 24 import android.security.keymaster.KeyCharacteristics; 25 import android.security.keymaster.KeymasterDefs; 26 27 import java.io.IOException; 28 import java.security.KeyFactory; 29 import java.security.KeyPair; 30 import java.security.KeyStoreException; 31 import java.security.NoSuchAlgorithmException; 32 import java.security.NoSuchProviderException; 33 import java.security.Provider; 34 import java.security.ProviderException; 35 import java.security.PublicKey; 36 import java.security.Security; 37 import java.security.Signature; 38 import java.security.UnrecoverableKeyException; 39 import java.security.cert.CertificateException; 40 import java.security.interfaces.ECKey; 41 import java.security.interfaces.ECPublicKey; 42 import java.security.interfaces.RSAKey; 43 import java.security.interfaces.RSAPublicKey; 44 import java.security.spec.InvalidKeySpecException; 45 import java.security.spec.X509EncodedKeySpec; 46 import java.util.List; 47 48 import javax.crypto.Cipher; 49 import javax.crypto.Mac; 50 51 /** 52 * A provider focused on providing JCA interfaces for the Android KeyStore. 53 * 54 * @hide 55 */ 56 @SystemApi 57 public class AndroidKeyStoreProvider extends Provider { 58 private static final String PROVIDER_NAME = "AndroidKeyStore"; 59 60 // IMPLEMENTATION NOTE: Class names are hard-coded in this provider to avoid loading these 61 // classes when this provider is instantiated and installed early on during each app's 62 // initialization process. 63 // 64 // Crypto operations operating on the AndroidKeyStore keys must not be offered by this provider. 65 // Instead, they need to be offered by AndroidKeyStoreBCWorkaroundProvider. See its Javadoc 66 // for details. 67 68 private static final String PACKAGE_NAME = "android.security.keystore"; 69 70 private static final String DESEDE_SYSTEM_PROPERTY = 71 "ro.hardware.keystore_desede"; 72 73 /** @hide **/ AndroidKeyStoreProvider()74 public AndroidKeyStoreProvider() { 75 super(PROVIDER_NAME, 1.0, "Android KeyStore security provider"); 76 77 boolean supports3DES = "true".equals(android.os.SystemProperties.get(DESEDE_SYSTEM_PROPERTY)); 78 79 // java.security.KeyStore 80 put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi"); 81 82 // java.security.KeyPairGenerator 83 put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC"); 84 put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA"); 85 86 // java.security.KeyFactory 87 putKeyFactoryImpl("EC"); 88 putKeyFactoryImpl("RSA"); 89 90 // javax.crypto.KeyGenerator 91 put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES"); 92 put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1"); 93 put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA224"); 94 put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA256"); 95 put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA384"); 96 put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA512"); 97 98 if (supports3DES) { 99 put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede"); 100 } 101 102 // java.security.SecretKeyFactory 103 putSecretKeyFactoryImpl("AES"); 104 if (supports3DES) { 105 putSecretKeyFactoryImpl("DESede"); 106 } 107 putSecretKeyFactoryImpl("HmacSHA1"); 108 putSecretKeyFactoryImpl("HmacSHA224"); 109 putSecretKeyFactoryImpl("HmacSHA256"); 110 putSecretKeyFactoryImpl("HmacSHA384"); 111 putSecretKeyFactoryImpl("HmacSHA512"); 112 } 113 114 /** 115 * Installs a new instance of this provider (and the 116 * {@link AndroidKeyStoreBCWorkaroundProvider}). 117 * @hide 118 */ install()119 public static void install() { 120 Provider[] providers = Security.getProviders(); 121 int bcProviderIndex = -1; 122 for (int i = 0; i < providers.length; i++) { 123 Provider provider = providers[i]; 124 if ("BC".equals(provider.getName())) { 125 bcProviderIndex = i; 126 break; 127 } 128 } 129 130 Security.addProvider(new AndroidKeyStoreProvider()); 131 Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider(); 132 if (bcProviderIndex != -1) { 133 // Bouncy Castle provider found -- install the workaround provider above it. 134 // insertProviderAt uses 1-based positions. 135 Security.insertProviderAt(workaroundProvider, bcProviderIndex + 1); 136 } else { 137 // Bouncy Castle provider not found -- install the workaround provider at lowest 138 // priority. 139 Security.addProvider(workaroundProvider); 140 } 141 } 142 putSecretKeyFactoryImpl(String algorithm)143 private void putSecretKeyFactoryImpl(String algorithm) { 144 put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreSecretKeyFactorySpi"); 145 } 146 putKeyFactoryImpl(String algorithm)147 private void putKeyFactoryImpl(String algorithm) { 148 put("KeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreKeyFactorySpi"); 149 } 150 151 /** 152 * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto 153 * primitive. 154 * 155 * <p>The following primitives are supported: {@link Cipher} and {@link Mac}. 156 * 157 * @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation 158 * is not in progress. 159 * 160 * @throws IllegalArgumentException if the provided primitive is not supported or is not backed 161 * by AndroidKeyStore provider. 162 * @throws IllegalStateException if the provided primitive is not initialized. 163 * @hide 164 */ 165 @UnsupportedAppUsage getKeyStoreOperationHandle(Object cryptoPrimitive)166 public static long getKeyStoreOperationHandle(Object cryptoPrimitive) { 167 if (cryptoPrimitive == null) { 168 throw new NullPointerException(); 169 } 170 Object spi; 171 if (cryptoPrimitive instanceof Signature) { 172 spi = ((Signature) cryptoPrimitive).getCurrentSpi(); 173 } else if (cryptoPrimitive instanceof Mac) { 174 spi = ((Mac) cryptoPrimitive).getCurrentSpi(); 175 } else if (cryptoPrimitive instanceof Cipher) { 176 spi = ((Cipher) cryptoPrimitive).getCurrentSpi(); 177 } else { 178 throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive 179 + ". Supported: Signature, Mac, Cipher"); 180 } 181 if (spi == null) { 182 throw new IllegalStateException("Crypto primitive not initialized"); 183 } else if (!(spi instanceof KeyStoreCryptoOperation)) { 184 throw new IllegalArgumentException( 185 "Crypto primitive not backed by AndroidKeyStore provider: " + cryptoPrimitive 186 + ", spi: " + spi); 187 } 188 return ((KeyStoreCryptoOperation) spi).getOperationHandle(); 189 } 190 191 /** @hide **/ 192 @NonNull getAndroidKeyStorePublicKey( @onNull String alias, int uid, @NonNull @KeyProperties.KeyAlgorithmEnum String keyAlgorithm, @NonNull byte[] x509EncodedForm)193 public static AndroidKeyStorePublicKey getAndroidKeyStorePublicKey( 194 @NonNull String alias, 195 int uid, 196 @NonNull @KeyProperties.KeyAlgorithmEnum String keyAlgorithm, 197 @NonNull byte[] x509EncodedForm) { 198 PublicKey publicKey; 199 try { 200 KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm); 201 publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedForm)); 202 } catch (NoSuchAlgorithmException e) { 203 throw new ProviderException( 204 "Failed to obtain " + keyAlgorithm + " KeyFactory", e); 205 } catch (InvalidKeySpecException e) { 206 throw new ProviderException("Invalid X.509 encoding of public key", e); 207 } 208 if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) { 209 return new AndroidKeyStoreECPublicKey(alias, uid, (ECPublicKey) publicKey); 210 } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { 211 return new AndroidKeyStoreRSAPublicKey(alias, uid, (RSAPublicKey) publicKey); 212 } else { 213 throw new ProviderException("Unsupported Android Keystore public key algorithm: " 214 + keyAlgorithm); 215 } 216 } 217 218 @NonNull getAndroidKeyStorePrivateKey( @onNull AndroidKeyStorePublicKey publicKey)219 private static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey( 220 @NonNull AndroidKeyStorePublicKey publicKey) { 221 String keyAlgorithm = publicKey.getAlgorithm(); 222 if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) { 223 return new AndroidKeyStoreECPrivateKey( 224 publicKey.getAlias(), publicKey.getUid(), ((ECKey) publicKey).getParams()); 225 } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { 226 return new AndroidKeyStoreRSAPrivateKey( 227 publicKey.getAlias(), publicKey.getUid(), ((RSAKey) publicKey).getModulus()); 228 } else { 229 throw new ProviderException("Unsupported Android Keystore public key algorithm: " 230 + keyAlgorithm); 231 } 232 } 233 234 @NonNull getKeyCharacteristics(@onNull KeyStore keyStore, @NonNull String alias, int uid)235 private static KeyCharacteristics getKeyCharacteristics(@NonNull KeyStore keyStore, 236 @NonNull String alias, int uid) 237 throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { 238 KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); 239 int errorCode = keyStore.getKeyCharacteristics( 240 alias, null, null, uid, keyCharacteristics); 241 if (errorCode == KeyStore.KEY_PERMANENTLY_INVALIDATED) { 242 throw (KeyPermanentlyInvalidatedException) 243 new KeyPermanentlyInvalidatedException( 244 "User changed or deleted their auth credentials", 245 KeyStore.getKeyStoreException(errorCode)); 246 } 247 if (errorCode != KeyStore.NO_ERROR) { 248 throw (UnrecoverableKeyException) 249 new UnrecoverableKeyException("Failed to obtain information about key") 250 .initCause(KeyStore.getKeyStoreException(errorCode)); 251 } 252 return keyCharacteristics; 253 } 254 255 @NonNull loadAndroidKeyStorePublicKeyFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, KeyCharacteristics keyCharacteristics)256 private static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore( 257 @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, 258 KeyCharacteristics keyCharacteristics) 259 throws UnrecoverableKeyException { 260 ExportResult exportResult = keyStore.exportKey( 261 privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null, uid); 262 if (exportResult.resultCode != KeyStore.NO_ERROR) { 263 throw (UnrecoverableKeyException) 264 new UnrecoverableKeyException("Failed to obtain X.509 form of public key") 265 .initCause(KeyStore.getKeyStoreException(exportResult.resultCode)); 266 } 267 final byte[] x509EncodedPublicKey = exportResult.exportData; 268 269 Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM); 270 if (keymasterAlgorithm == null) { 271 throw new UnrecoverableKeyException("Key algorithm unknown"); 272 } 273 274 String jcaKeyAlgorithm; 275 try { 276 jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm( 277 keymasterAlgorithm); 278 } catch (IllegalArgumentException e) { 279 throw (UnrecoverableKeyException) 280 new UnrecoverableKeyException("Failed to load private key") 281 .initCause(e); 282 } 283 284 return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey( 285 privateKeyAlias, uid, jcaKeyAlgorithm, x509EncodedPublicKey); 286 } 287 288 /** @hide **/ 289 @NonNull loadAndroidKeyStorePublicKeyFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)290 public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore( 291 @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid) 292 throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { 293 return loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid, 294 getKeyCharacteristics(keyStore, privateKeyAlias, uid)); 295 } 296 297 @NonNull loadAndroidKeyStoreKeyPairFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics)298 private static KeyPair loadAndroidKeyStoreKeyPairFromKeystore( 299 @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, 300 @NonNull KeyCharacteristics keyCharacteristics) 301 throws UnrecoverableKeyException { 302 AndroidKeyStorePublicKey publicKey = 303 loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid, 304 keyCharacteristics); 305 AndroidKeyStorePrivateKey privateKey = 306 AndroidKeyStoreProvider.getAndroidKeyStorePrivateKey(publicKey); 307 return new KeyPair(publicKey, privateKey); 308 } 309 310 /** @hide **/ 311 @NonNull loadAndroidKeyStoreKeyPairFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)312 public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore( 313 @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid) 314 throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { 315 return loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid, 316 getKeyCharacteristics(keyStore, privateKeyAlias, uid)); 317 } 318 319 @NonNull loadAndroidKeyStorePrivateKeyFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics)320 private static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore( 321 @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, 322 @NonNull KeyCharacteristics keyCharacteristics) 323 throws UnrecoverableKeyException { 324 KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid, 325 keyCharacteristics); 326 return (AndroidKeyStorePrivateKey) keyPair.getPrivate(); 327 } 328 329 /** @hide **/ 330 @NonNull loadAndroidKeyStorePrivateKeyFromKeystore( @onNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)331 public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore( 332 @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid) 333 throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { 334 return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, privateKeyAlias, uid, 335 getKeyCharacteristics(keyStore, privateKeyAlias, uid)); 336 } 337 338 @NonNull loadAndroidKeyStoreSecretKeyFromKeystore( @onNull String secretKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics)339 private static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore( 340 @NonNull String secretKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics) 341 throws UnrecoverableKeyException { 342 Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM); 343 if (keymasterAlgorithm == null) { 344 throw new UnrecoverableKeyException("Key algorithm unknown"); 345 } 346 347 List<Integer> keymasterDigests = keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST); 348 int keymasterDigest; 349 if (keymasterDigests.isEmpty()) { 350 keymasterDigest = -1; 351 } else { 352 // More than one digest can be permitted for this key. Use the first one to form the 353 // JCA key algorithm name. 354 keymasterDigest = keymasterDigests.get(0); 355 } 356 357 @KeyProperties.KeyAlgorithmEnum String keyAlgorithmString; 358 try { 359 keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm( 360 keymasterAlgorithm, keymasterDigest); 361 } catch (IllegalArgumentException e) { 362 throw (UnrecoverableKeyException) 363 new UnrecoverableKeyException("Unsupported secret key type").initCause(e); 364 } 365 366 return new AndroidKeyStoreSecretKey(secretKeyAlias, uid, keyAlgorithmString); 367 } 368 369 /** @hide **/ 370 @NonNull loadAndroidKeyStoreKeyFromKeystore( @onNull KeyStore keyStore, @NonNull String userKeyAlias, int uid)371 public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore( 372 @NonNull KeyStore keyStore, @NonNull String userKeyAlias, int uid) 373 throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { 374 KeyCharacteristics keyCharacteristics = getKeyCharacteristics(keyStore, userKeyAlias, uid); 375 376 Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM); 377 if (keymasterAlgorithm == null) { 378 throw new UnrecoverableKeyException("Key algorithm unknown"); 379 } 380 381 if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC || 382 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES || 383 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) { 384 return loadAndroidKeyStoreSecretKeyFromKeystore(userKeyAlias, uid, 385 keyCharacteristics); 386 } else if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA || 387 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) { 388 return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, userKeyAlias, uid, 389 keyCharacteristics); 390 } else { 391 throw new UnrecoverableKeyException("Key algorithm unknown"); 392 } 393 } 394 395 /** 396 * Returns an {@code AndroidKeyStore} {@link java.security.KeyStore}} of the specified UID. 397 * The {@code KeyStore} contains keys and certificates owned by that UID. Such cross-UID 398 * access is permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN) 399 * all of which are system. 400 * 401 * <p>Note: the returned {@code KeyStore} is already initialized/loaded. Thus, there is 402 * no need to invoke {@code load} on it. 403 * 404 * @param uid Uid for which the keystore provider is requested. 405 * @throws KeyStoreException if a KeyStoreSpi implementation for the specified type is not 406 * available from the specified provider. 407 * @throws NoSuchProviderException If the specified provider is not registered in the security 408 * provider list. 409 * @hide 410 */ 411 @SystemApi 412 @NonNull getKeyStoreForUid(int uid)413 public static java.security.KeyStore getKeyStoreForUid(int uid) 414 throws KeyStoreException, NoSuchProviderException { 415 java.security.KeyStore result = 416 java.security.KeyStore.getInstance("AndroidKeyStore", PROVIDER_NAME); 417 try { 418 result.load(new AndroidKeyStoreLoadStoreParameter(uid)); 419 } catch (NoSuchAlgorithmException | CertificateException | IOException e) { 420 throw new KeyStoreException( 421 "Failed to load AndroidKeyStore KeyStore for UID " + uid, e); 422 } 423 return result; 424 } 425 } 426