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