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 17package android.security.keystore; 18 19import libcore.util.EmptyArray; 20import android.security.Credentials; 21import android.security.KeyStore; 22import android.security.KeyStoreParameter; 23import android.security.keymaster.KeyCharacteristics; 24import android.security.keymaster.KeymasterArguments; 25import android.security.keymaster.KeymasterDefs; 26import android.security.keystore.KeyProperties; 27import android.security.keystore.KeyProtection; 28import android.util.Log; 29 30import java.io.ByteArrayInputStream; 31import java.io.IOException; 32import java.io.InputStream; 33import java.io.OutputStream; 34import java.security.Key; 35import java.security.KeyStore.Entry; 36import java.security.KeyStore.LoadStoreParameter; 37import java.security.KeyStore.PrivateKeyEntry; 38import java.security.KeyStore.ProtectionParameter; 39import java.security.KeyStore.SecretKeyEntry; 40import java.security.KeyStoreException; 41import java.security.KeyStoreSpi; 42import java.security.NoSuchAlgorithmException; 43import java.security.PrivateKey; 44import java.security.ProviderException; 45import java.security.PublicKey; 46import java.security.UnrecoverableKeyException; 47import java.security.cert.Certificate; 48import java.security.cert.CertificateEncodingException; 49import java.security.cert.CertificateException; 50import java.security.cert.CertificateFactory; 51import java.security.cert.X509Certificate; 52import java.util.ArrayList; 53import java.util.Arrays; 54import java.util.Collection; 55import java.util.Collections; 56import java.util.Date; 57import java.util.Enumeration; 58import java.util.HashSet; 59import java.util.Iterator; 60import java.util.Set; 61 62import javax.crypto.SecretKey; 63 64/** 65 * A java.security.KeyStore interface for the Android KeyStore. An instance of 66 * it can be created via the {@link java.security.KeyStore#getInstance(String) 67 * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a 68 * java.security.KeyStore backed by this "AndroidKeyStore" implementation. 69 * <p> 70 * This is built on top of Android's keystore daemon. The convention of alias 71 * use is: 72 * <p> 73 * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key, 74 * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one 75 * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE 76 * entry which will have the rest of the chain concatenated in BER format. 77 * <p> 78 * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry 79 * with a single certificate. 80 * 81 * @hide 82 */ 83public class AndroidKeyStoreSpi extends KeyStoreSpi { 84 public static final String NAME = "AndroidKeyStore"; 85 86 private KeyStore mKeyStore; 87 private int mUid = KeyStore.UID_SELF; 88 89 @Override 90 public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, 91 UnrecoverableKeyException { 92 if (isPrivateKeyEntry(alias)) { 93 String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias; 94 return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore( 95 mKeyStore, privateKeyAlias, mUid); 96 } else if (isSecretKeyEntry(alias)) { 97 String secretKeyAlias = Credentials.USER_SECRET_KEY + alias; 98 return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore( 99 mKeyStore, secretKeyAlias, mUid); 100 } else { 101 // Key not found 102 return null; 103 } 104 } 105 106 @Override 107 public Certificate[] engineGetCertificateChain(String alias) { 108 if (alias == null) { 109 throw new NullPointerException("alias == null"); 110 } 111 112 final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias); 113 if (leaf == null) { 114 return null; 115 } 116 117 final Certificate[] caList; 118 119 final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid); 120 if (caBytes != null) { 121 final Collection<X509Certificate> caChain = toCertificates(caBytes); 122 123 caList = new Certificate[caChain.size() + 1]; 124 125 final Iterator<X509Certificate> it = caChain.iterator(); 126 int i = 1; 127 while (it.hasNext()) { 128 caList[i++] = it.next(); 129 } 130 } else { 131 caList = new Certificate[1]; 132 } 133 134 caList[0] = leaf; 135 136 return caList; 137 } 138 139 @Override 140 public Certificate engineGetCertificate(String alias) { 141 if (alias == null) { 142 throw new NullPointerException("alias == null"); 143 } 144 145 byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid); 146 if (encodedCert != null) { 147 return getCertificateForPrivateKeyEntry(alias, encodedCert); 148 } 149 150 encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid); 151 if (encodedCert != null) { 152 return getCertificateForTrustedCertificateEntry(encodedCert); 153 } 154 155 // This entry/alias does not contain a certificate. 156 return null; 157 } 158 159 private Certificate getCertificateForTrustedCertificateEntry(byte[] encodedCert) { 160 // For this certificate there shouldn't be a private key in this KeyStore entry. Thus, 161 // there's no need to wrap this certificate as opposed to the certificate associated with 162 // a private key entry. 163 return toCertificate(encodedCert); 164 } 165 166 private Certificate getCertificateForPrivateKeyEntry(String alias, byte[] encodedCert) { 167 // All crypto algorithms offered by Android Keystore for its private keys must also 168 // be offered for the corresponding public keys stored in the Android Keystore. The 169 // complication is that the underlying keystore service operates only on full key pairs, 170 // rather than just public keys or private keys. As a result, Android Keystore-backed 171 // crypto can only be offered for public keys for which keystore contains the 172 // corresponding private key. This is not the case for certificate-only entries (e.g., 173 // trusted certificates). 174 // 175 // getCertificate().getPublicKey() is the only way to obtain the public key 176 // corresponding to the private key stored in the KeyStore. Thus, we need to make sure 177 // that the returned public key points to the underlying key pair / private key 178 // when available. 179 180 X509Certificate cert = toCertificate(encodedCert); 181 if (cert == null) { 182 // Failed to parse the certificate. 183 return null; 184 } 185 186 String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias; 187 if (mKeyStore.contains(privateKeyAlias, mUid)) { 188 // As expected, keystore contains the private key corresponding to this public key. Wrap 189 // the certificate so that its getPublicKey method returns an Android Keystore 190 // PublicKey. This key will delegate crypto operations involving this public key to 191 // Android Keystore when higher-priority providers do not offer these crypto 192 // operations for this key. 193 return wrapIntoKeyStoreCertificate(privateKeyAlias, mUid, cert); 194 } else { 195 // This KeyStore entry/alias is supposed to contain the private key corresponding to 196 // the public key in this certificate, but it does not for some reason. It's probably a 197 // bug. Let other providers handle crypto operations involving the public key returned 198 // by this certificate's getPublicKey. 199 return cert; 200 } 201 } 202 203 /** 204 * Wraps the provided cerificate into {@link KeyStoreX509Certificate} so that the public key 205 * returned by the certificate contains information about the alias of the private key in 206 * keystore. This is needed so that Android Keystore crypto operations using public keys can 207 * find out which key alias to use. These operations cannot work without an alias. 208 */ 209 private static KeyStoreX509Certificate wrapIntoKeyStoreCertificate( 210 String privateKeyAlias, int uid, X509Certificate certificate) { 211 return (certificate != null) 212 ? new KeyStoreX509Certificate(privateKeyAlias, uid, certificate) : null; 213 } 214 215 private static X509Certificate toCertificate(byte[] bytes) { 216 try { 217 final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 218 return (X509Certificate) certFactory.generateCertificate( 219 new ByteArrayInputStream(bytes)); 220 } catch (CertificateException e) { 221 Log.w(NAME, "Couldn't parse certificate in keystore", e); 222 return null; 223 } 224 } 225 226 @SuppressWarnings("unchecked") 227 private static Collection<X509Certificate> toCertificates(byte[] bytes) { 228 try { 229 final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 230 return (Collection<X509Certificate>) certFactory.generateCertificates( 231 new ByteArrayInputStream(bytes)); 232 } catch (CertificateException e) { 233 Log.w(NAME, "Couldn't parse certificates in keystore", e); 234 return new ArrayList<X509Certificate>(); 235 } 236 } 237 238 private Date getModificationDate(String alias) { 239 final long epochMillis = mKeyStore.getmtime(alias, mUid); 240 if (epochMillis == -1L) { 241 return null; 242 } 243 244 return new Date(epochMillis); 245 } 246 247 @Override 248 public Date engineGetCreationDate(String alias) { 249 if (alias == null) { 250 throw new NullPointerException("alias == null"); 251 } 252 253 Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias); 254 if (d != null) { 255 return d; 256 } 257 258 d = getModificationDate(Credentials.USER_SECRET_KEY + alias); 259 if (d != null) { 260 return d; 261 } 262 263 d = getModificationDate(Credentials.USER_CERTIFICATE + alias); 264 if (d != null) { 265 return d; 266 } 267 268 return getModificationDate(Credentials.CA_CERTIFICATE + alias); 269 } 270 271 @Override 272 public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) 273 throws KeyStoreException { 274 if ((password != null) && (password.length > 0)) { 275 throw new KeyStoreException("entries cannot be protected with passwords"); 276 } 277 278 if (key instanceof PrivateKey) { 279 setPrivateKeyEntry(alias, (PrivateKey) key, chain, null); 280 } else if (key instanceof SecretKey) { 281 setSecretKeyEntry(alias, (SecretKey) key, null); 282 } else { 283 throw new KeyStoreException("Only PrivateKey and SecretKey are supported"); 284 } 285 } 286 287 private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key) 288 throws KeyStoreException { 289 String keyAlgorithm = key.getAlgorithm(); 290 KeyProtection.Builder specBuilder; 291 if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) { 292 specBuilder = 293 new KeyProtection.Builder( 294 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY); 295 // Authorized to be used with any digest (including no digest). 296 // MD5 was never offered for Android Keystore for ECDSA. 297 specBuilder.setDigests( 298 KeyProperties.DIGEST_NONE, 299 KeyProperties.DIGEST_SHA1, 300 KeyProperties.DIGEST_SHA224, 301 KeyProperties.DIGEST_SHA256, 302 KeyProperties.DIGEST_SHA384, 303 KeyProperties.DIGEST_SHA512); 304 } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { 305 specBuilder = 306 new KeyProtection.Builder( 307 KeyProperties.PURPOSE_ENCRYPT 308 | KeyProperties.PURPOSE_DECRYPT 309 | KeyProperties.PURPOSE_SIGN 310 | KeyProperties.PURPOSE_VERIFY); 311 // Authorized to be used with any digest (including no digest). 312 specBuilder.setDigests( 313 KeyProperties.DIGEST_NONE, 314 KeyProperties.DIGEST_MD5, 315 KeyProperties.DIGEST_SHA1, 316 KeyProperties.DIGEST_SHA224, 317 KeyProperties.DIGEST_SHA256, 318 KeyProperties.DIGEST_SHA384, 319 KeyProperties.DIGEST_SHA512); 320 // Authorized to be used with any encryption and signature padding 321 // schemes (including no padding). 322 specBuilder.setEncryptionPaddings( 323 KeyProperties.ENCRYPTION_PADDING_NONE, 324 KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1, 325 KeyProperties.ENCRYPTION_PADDING_RSA_OAEP); 326 specBuilder.setSignaturePaddings( 327 KeyProperties.SIGNATURE_PADDING_RSA_PKCS1, 328 KeyProperties.SIGNATURE_PADDING_RSA_PSS); 329 // Disable randomized encryption requirement to support encryption 330 // padding NONE above. 331 specBuilder.setRandomizedEncryptionRequired(false); 332 } else { 333 throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm); 334 } 335 specBuilder.setUserAuthenticationRequired(false); 336 337 return specBuilder.build(); 338 } 339 340 private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain, 341 java.security.KeyStore.ProtectionParameter param) throws KeyStoreException { 342 int flags = 0; 343 KeyProtection spec; 344 if (param == null) { 345 spec = getLegacyKeyProtectionParameter(key); 346 } else if (param instanceof KeyStoreParameter) { 347 spec = getLegacyKeyProtectionParameter(key); 348 KeyStoreParameter legacySpec = (KeyStoreParameter) param; 349 if (legacySpec.isEncryptionRequired()) { 350 flags = KeyStore.FLAG_ENCRYPTED; 351 } 352 } else if (param instanceof KeyProtection) { 353 spec = (KeyProtection) param; 354 } else { 355 throw new KeyStoreException( 356 "Unsupported protection parameter class:" + param.getClass().getName() 357 + ". Supported: " + KeyProtection.class.getName() + ", " 358 + KeyStoreParameter.class.getName()); 359 } 360 361 // Make sure the chain exists since this is a PrivateKey 362 if ((chain == null) || (chain.length == 0)) { 363 throw new KeyStoreException("Must supply at least one Certificate with PrivateKey"); 364 } 365 366 // Do chain type checking. 367 X509Certificate[] x509chain = new X509Certificate[chain.length]; 368 for (int i = 0; i < chain.length; i++) { 369 if (!"X.509".equals(chain[i].getType())) { 370 throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #" 371 + i); 372 } 373 374 if (!(chain[i] instanceof X509Certificate)) { 375 throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #" 376 + i); 377 } 378 379 x509chain[i] = (X509Certificate) chain[i]; 380 } 381 382 final byte[] userCertBytes; 383 try { 384 userCertBytes = x509chain[0].getEncoded(); 385 } catch (CertificateEncodingException e) { 386 throw new KeyStoreException("Failed to encode certificate #0", e); 387 } 388 389 /* 390 * If we have a chain, store it in the CA certificate slot for this 391 * alias as concatenated DER-encoded certificates. These can be 392 * deserialized by {@link CertificateFactory#generateCertificates}. 393 */ 394 final byte[] chainBytes; 395 if (chain.length > 1) { 396 /* 397 * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...} 398 * so we only need the certificates starting at index 1. 399 */ 400 final byte[][] certsBytes = new byte[x509chain.length - 1][]; 401 int totalCertLength = 0; 402 for (int i = 0; i < certsBytes.length; i++) { 403 try { 404 certsBytes[i] = x509chain[i + 1].getEncoded(); 405 totalCertLength += certsBytes[i].length; 406 } catch (CertificateEncodingException e) { 407 throw new KeyStoreException("Failed to encode certificate #" + i, e); 408 } 409 } 410 411 /* 412 * Serialize this into one byte array so we can later call 413 * CertificateFactory#generateCertificates to recover them. 414 */ 415 chainBytes = new byte[totalCertLength]; 416 int outputOffset = 0; 417 for (int i = 0; i < certsBytes.length; i++) { 418 final int certLength = certsBytes[i].length; 419 System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength); 420 outputOffset += certLength; 421 certsBytes[i] = null; 422 } 423 } else { 424 chainBytes = null; 425 } 426 427 final String pkeyAlias; 428 if (key instanceof AndroidKeyStorePrivateKey) { 429 pkeyAlias = ((AndroidKeyStoreKey) key).getAlias(); 430 } else { 431 pkeyAlias = null; 432 } 433 434 byte[] pkcs8EncodedPrivateKeyBytes; 435 KeymasterArguments importArgs; 436 final boolean shouldReplacePrivateKey; 437 if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) { 438 final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length()); 439 if (!alias.equals(keySubalias)) { 440 throw new KeyStoreException("Can only replace keys with same alias: " + alias 441 + " != " + keySubalias); 442 } 443 shouldReplacePrivateKey = false; 444 importArgs = null; 445 pkcs8EncodedPrivateKeyBytes = null; 446 } else { 447 shouldReplacePrivateKey = true; 448 // Make sure the PrivateKey format is the one we support. 449 final String keyFormat = key.getFormat(); 450 if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) { 451 throw new KeyStoreException( 452 "Unsupported private key export format: " + keyFormat 453 + ". Only private keys which export their key material in PKCS#8 format are" 454 + " supported."); 455 } 456 457 // Make sure we can actually encode the key. 458 pkcs8EncodedPrivateKeyBytes = key.getEncoded(); 459 if (pkcs8EncodedPrivateKeyBytes == null) { 460 throw new KeyStoreException("Private key did not export any key material"); 461 } 462 463 importArgs = new KeymasterArguments(); 464 try { 465 importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, 466 KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm( 467 key.getAlgorithm())); 468 @KeyProperties.PurposeEnum int purposes = spec.getPurposes(); 469 importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE, 470 KeyProperties.Purpose.allToKeymaster(purposes)); 471 if (spec.isDigestsSpecified()) { 472 importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST, 473 KeyProperties.Digest.allToKeymaster(spec.getDigests())); 474 } 475 476 importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, 477 KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes())); 478 int[] keymasterEncryptionPaddings = 479 KeyProperties.EncryptionPadding.allToKeymaster( 480 spec.getEncryptionPaddings()); 481 if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) 482 && (spec.isRandomizedEncryptionRequired())) { 483 for (int keymasterPadding : keymasterEncryptionPaddings) { 484 if (!KeymasterUtils 485 .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( 486 keymasterPadding)) { 487 throw new KeyStoreException( 488 "Randomized encryption (IND-CPA) required but is violated by" 489 + " encryption padding mode: " 490 + KeyProperties.EncryptionPadding.fromKeymaster( 491 keymasterPadding) 492 + ". See KeyProtection documentation."); 493 } 494 } 495 } 496 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings); 497 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, 498 KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings())); 499 KeymasterUtils.addUserAuthArgs(importArgs, 500 spec.isUserAuthenticationRequired(), 501 spec.getUserAuthenticationValidityDurationSeconds(), 502 spec.isUserAuthenticationValidWhileOnBody(), 503 spec.isInvalidatedByBiometricEnrollment()); 504 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, 505 spec.getKeyValidityStart()); 506 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, 507 spec.getKeyValidityForOriginationEnd()); 508 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, 509 spec.getKeyValidityForConsumptionEnd()); 510 } catch (IllegalArgumentException | IllegalStateException e) { 511 throw new KeyStoreException(e); 512 } 513 } 514 515 516 boolean success = false; 517 try { 518 // Store the private key, if necessary 519 if (shouldReplacePrivateKey) { 520 // Delete the stored private key and any related entries before importing the 521 // provided key 522 Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid); 523 KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics(); 524 int errorCode = mKeyStore.importKey( 525 Credentials.USER_PRIVATE_KEY + alias, 526 importArgs, 527 KeymasterDefs.KM_KEY_FORMAT_PKCS8, 528 pkcs8EncodedPrivateKeyBytes, 529 mUid, 530 flags, 531 resultingKeyCharacteristics); 532 if (errorCode != KeyStore.NO_ERROR) { 533 throw new KeyStoreException("Failed to store private key", 534 KeyStore.getKeyStoreException(errorCode)); 535 } 536 } else { 537 // Keep the stored private key around -- delete all other entry types 538 Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid); 539 Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid); 540 } 541 542 // Store the leaf certificate 543 int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes, 544 mUid, flags); 545 if (errorCode != KeyStore.NO_ERROR) { 546 throw new KeyStoreException("Failed to store certificate #0", 547 KeyStore.getKeyStoreException(errorCode)); 548 } 549 550 // Store the certificate chain 551 errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes, 552 mUid, flags); 553 if (errorCode != KeyStore.NO_ERROR) { 554 throw new KeyStoreException("Failed to store certificate chain", 555 KeyStore.getKeyStoreException(errorCode)); 556 } 557 success = true; 558 } finally { 559 if (!success) { 560 if (shouldReplacePrivateKey) { 561 Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid); 562 } else { 563 Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid); 564 Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid); 565 } 566 } 567 } 568 } 569 570 private void setSecretKeyEntry(String entryAlias, SecretKey key, 571 java.security.KeyStore.ProtectionParameter param) 572 throws KeyStoreException { 573 if ((param != null) && (!(param instanceof KeyProtection))) { 574 throw new KeyStoreException( 575 "Unsupported protection parameter class: " + param.getClass().getName() 576 + ". Supported: " + KeyProtection.class.getName()); 577 } 578 KeyProtection params = (KeyProtection) param; 579 580 if (key instanceof AndroidKeyStoreSecretKey) { 581 // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot 582 // overwrite its own entry. 583 String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias(); 584 if (keyAliasInKeystore == null) { 585 throw new KeyStoreException("KeyStore-backed secret key does not have an alias"); 586 } 587 if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) { 588 throw new KeyStoreException("KeyStore-backed secret key has invalid alias: " 589 + keyAliasInKeystore); 590 } 591 String keyEntryAlias = 592 keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length()); 593 if (!entryAlias.equals(keyEntryAlias)) { 594 throw new KeyStoreException("Can only replace KeyStore-backed keys with same" 595 + " alias: " + entryAlias + " != " + keyEntryAlias); 596 } 597 // This is the entry where this key is already stored. No need to do anything. 598 if (params != null) { 599 throw new KeyStoreException("Modifying KeyStore-backed key using protection" 600 + " parameters not supported"); 601 } 602 return; 603 } 604 605 if (params == null) { 606 throw new KeyStoreException( 607 "Protection parameters must be specified when importing a symmetric key"); 608 } 609 610 // Not a KeyStore-backed secret key -- import its key material into keystore. 611 String keyExportFormat = key.getFormat(); 612 if (keyExportFormat == null) { 613 throw new KeyStoreException( 614 "Only secret keys that export their key material are supported"); 615 } else if (!"RAW".equals(keyExportFormat)) { 616 throw new KeyStoreException( 617 "Unsupported secret key material export format: " + keyExportFormat); 618 } 619 byte[] keyMaterial = key.getEncoded(); 620 if (keyMaterial == null) { 621 throw new KeyStoreException("Key did not export its key material despite supporting" 622 + " RAW format export"); 623 } 624 625 KeymasterArguments args = new KeymasterArguments(); 626 try { 627 int keymasterAlgorithm = 628 KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(key.getAlgorithm()); 629 args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm); 630 631 int[] keymasterDigests; 632 if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { 633 // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm 634 // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one 635 // digest, we don't let import parameters override the digest implied by the key. 636 // If the parameters specify digests at all, they must specify only one digest, the 637 // only implied by key algorithm. 638 int keymasterImpliedDigest = 639 KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm()); 640 if (keymasterImpliedDigest == -1) { 641 throw new ProviderException( 642 "HMAC key algorithm digest unknown for key algorithm " 643 + key.getAlgorithm()); 644 } 645 keymasterDigests = new int[] {keymasterImpliedDigest}; 646 if (params.isDigestsSpecified()) { 647 // Digest(s) explicitly specified in params -- check that the list consists of 648 // exactly one digest, the one implied by key algorithm. 649 int[] keymasterDigestsFromParams = 650 KeyProperties.Digest.allToKeymaster(params.getDigests()); 651 if ((keymasterDigestsFromParams.length != 1) 652 || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) { 653 throw new KeyStoreException( 654 "Unsupported digests specification: " 655 + Arrays.asList(params.getDigests()) + ". Only " 656 + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest) 657 + " supported for HMAC key algorithm " + key.getAlgorithm()); 658 } 659 } 660 } else { 661 // Key algorithm does not imply a digest. 662 if (params.isDigestsSpecified()) { 663 keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests()); 664 } else { 665 keymasterDigests = EmptyArray.INT; 666 } 667 } 668 args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests); 669 670 @KeyProperties.PurposeEnum int purposes = params.getPurposes(); 671 int[] keymasterBlockModes = 672 KeyProperties.BlockMode.allToKeymaster(params.getBlockModes()); 673 if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) 674 && (params.isRandomizedEncryptionRequired())) { 675 for (int keymasterBlockMode : keymasterBlockModes) { 676 if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto( 677 keymasterBlockMode)) { 678 throw new KeyStoreException( 679 "Randomized encryption (IND-CPA) required but may be violated by" 680 + " block mode: " 681 + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode) 682 + ". See KeyProtection documentation."); 683 } 684 } 685 } 686 args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, 687 KeyProperties.Purpose.allToKeymaster(purposes)); 688 args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes); 689 if (params.getSignaturePaddings().length > 0) { 690 throw new KeyStoreException("Signature paddings not supported for symmetric keys"); 691 } 692 int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster( 693 params.getEncryptionPaddings()); 694 args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings); 695 KeymasterUtils.addUserAuthArgs(args, 696 params.isUserAuthenticationRequired(), 697 params.getUserAuthenticationValidityDurationSeconds(), 698 params.isUserAuthenticationValidWhileOnBody(), 699 params.isInvalidatedByBiometricEnrollment()); 700 KeymasterUtils.addMinMacLengthAuthorizationIfNecessary( 701 args, 702 keymasterAlgorithm, 703 keymasterBlockModes, 704 keymasterDigests); 705 args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, 706 params.getKeyValidityStart()); 707 args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, 708 params.getKeyValidityForOriginationEnd()); 709 args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, 710 params.getKeyValidityForConsumptionEnd()); 711 712 if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) 713 && (!params.isRandomizedEncryptionRequired())) { 714 // Permit caller-provided IV when encrypting with this key 715 args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); 716 } 717 } catch (IllegalArgumentException | IllegalStateException e) { 718 throw new KeyStoreException(e); 719 } 720 721 Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid); 722 String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias; 723 int errorCode = mKeyStore.importKey( 724 keyAliasInKeystore, 725 args, 726 KeymasterDefs.KM_KEY_FORMAT_RAW, 727 keyMaterial, 728 mUid, 729 0, // flags 730 new KeyCharacteristics()); 731 if (errorCode != KeyStore.NO_ERROR) { 732 throw new KeyStoreException("Failed to import secret key. Keystore error code: " 733 + errorCode); 734 } 735 } 736 737 @Override 738 public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain) 739 throws KeyStoreException { 740 throw new KeyStoreException("Operation not supported because key encoding is unknown"); 741 } 742 743 @Override 744 public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException { 745 if (isKeyEntry(alias)) { 746 throw new KeyStoreException("Entry exists and is not a trusted certificate"); 747 } 748 749 // We can't set something to null. 750 if (cert == null) { 751 throw new NullPointerException("cert == null"); 752 } 753 754 final byte[] encoded; 755 try { 756 encoded = cert.getEncoded(); 757 } catch (CertificateEncodingException e) { 758 throw new KeyStoreException(e); 759 } 760 761 if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, mUid, KeyStore.FLAG_NONE)) { 762 throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?"); 763 } 764 } 765 766 @Override 767 public void engineDeleteEntry(String alias) throws KeyStoreException { 768 if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid)) { 769 throw new KeyStoreException("Failed to delete entry: " + alias); 770 } 771 } 772 773 private Set<String> getUniqueAliases() { 774 final String[] rawAliases = mKeyStore.list("", mUid); 775 if (rawAliases == null) { 776 return new HashSet<String>(); 777 } 778 779 final Set<String> aliases = new HashSet<String>(rawAliases.length); 780 for (String alias : rawAliases) { 781 final int idx = alias.indexOf('_'); 782 if ((idx == -1) || (alias.length() <= idx)) { 783 Log.e(NAME, "invalid alias: " + alias); 784 continue; 785 } 786 787 aliases.add(new String(alias.substring(idx + 1))); 788 } 789 790 return aliases; 791 } 792 793 @Override 794 public Enumeration<String> engineAliases() { 795 return Collections.enumeration(getUniqueAliases()); 796 } 797 798 @Override 799 public boolean engineContainsAlias(String alias) { 800 if (alias == null) { 801 throw new NullPointerException("alias == null"); 802 } 803 804 return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid) 805 || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid) 806 || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias, mUid) 807 || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid); 808 } 809 810 @Override 811 public int engineSize() { 812 return getUniqueAliases().size(); 813 } 814 815 @Override 816 public boolean engineIsKeyEntry(String alias) { 817 return isKeyEntry(alias); 818 } 819 820 private boolean isKeyEntry(String alias) { 821 return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias); 822 } 823 824 private boolean isPrivateKeyEntry(String alias) { 825 if (alias == null) { 826 throw new NullPointerException("alias == null"); 827 } 828 829 return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid); 830 } 831 832 private boolean isSecretKeyEntry(String alias) { 833 if (alias == null) { 834 throw new NullPointerException("alias == null"); 835 } 836 837 return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid); 838 } 839 840 private boolean isCertificateEntry(String alias) { 841 if (alias == null) { 842 throw new NullPointerException("alias == null"); 843 } 844 845 return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid); 846 } 847 848 @Override 849 public boolean engineIsCertificateEntry(String alias) { 850 return !isKeyEntry(alias) && isCertificateEntry(alias); 851 } 852 853 @Override 854 public String engineGetCertificateAlias(Certificate cert) { 855 if (cert == null) { 856 return null; 857 } 858 if (!"X.509".equalsIgnoreCase(cert.getType())) { 859 // Only X.509 certificates supported 860 return null; 861 } 862 byte[] targetCertBytes; 863 try { 864 targetCertBytes = cert.getEncoded(); 865 } catch (CertificateEncodingException e) { 866 return null; 867 } 868 if (targetCertBytes == null) { 869 return null; 870 } 871 872 final Set<String> nonCaEntries = new HashSet<String>(); 873 874 /* 875 * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation 876 * says to only compare the first certificate in the chain which is 877 * equivalent to the USER_CERTIFICATE prefix for the Android keystore 878 * convention. 879 */ 880 final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE, mUid); 881 if (certAliases != null) { 882 for (String alias : certAliases) { 883 final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid); 884 if (certBytes == null) { 885 continue; 886 } 887 888 nonCaEntries.add(alias); 889 890 if (Arrays.equals(certBytes, targetCertBytes)) { 891 return alias; 892 } 893 } 894 } 895 896 /* 897 * Look at all the TrustedCertificateEntry types. Skip all the 898 * PrivateKeyEntry we looked at above. 899 */ 900 final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE, mUid); 901 if (certAliases != null) { 902 for (String alias : caAliases) { 903 if (nonCaEntries.contains(alias)) { 904 continue; 905 } 906 907 final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid); 908 if (certBytes == null) { 909 continue; 910 } 911 912 if (Arrays.equals(certBytes, targetCertBytes)) { 913 return alias; 914 } 915 } 916 } 917 918 return null; 919 } 920 921 @Override 922 public void engineStore(OutputStream stream, char[] password) throws IOException, 923 NoSuchAlgorithmException, CertificateException { 924 throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream"); 925 } 926 927 @Override 928 public void engineLoad(InputStream stream, char[] password) throws IOException, 929 NoSuchAlgorithmException, CertificateException { 930 if (stream != null) { 931 throw new IllegalArgumentException("InputStream not supported"); 932 } 933 934 if (password != null) { 935 throw new IllegalArgumentException("password not supported"); 936 } 937 938 // Unfortunate name collision. 939 mKeyStore = KeyStore.getInstance(); 940 mUid = KeyStore.UID_SELF; 941 } 942 943 @Override 944 public void engineLoad(LoadStoreParameter param) throws IOException, 945 NoSuchAlgorithmException, CertificateException { 946 int uid = KeyStore.UID_SELF; 947 if (param != null) { 948 if (param instanceof AndroidKeyStoreLoadStoreParameter) { 949 uid = ((AndroidKeyStoreLoadStoreParameter) param).getUid(); 950 } else { 951 throw new IllegalArgumentException( 952 "Unsupported param type: " + param.getClass()); 953 } 954 } 955 mKeyStore = KeyStore.getInstance(); 956 mUid = uid; 957 } 958 959 @Override 960 public void engineSetEntry(String alias, Entry entry, ProtectionParameter param) 961 throws KeyStoreException { 962 if (entry == null) { 963 throw new KeyStoreException("entry == null"); 964 } 965 966 Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid); 967 968 if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) { 969 java.security.KeyStore.TrustedCertificateEntry trE = 970 (java.security.KeyStore.TrustedCertificateEntry) entry; 971 engineSetCertificateEntry(alias, trE.getTrustedCertificate()); 972 return; 973 } 974 975 if (entry instanceof PrivateKeyEntry) { 976 PrivateKeyEntry prE = (PrivateKeyEntry) entry; 977 setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param); 978 } else if (entry instanceof SecretKeyEntry) { 979 SecretKeyEntry secE = (SecretKeyEntry) entry; 980 setSecretKeyEntry(alias, secE.getSecretKey(), param); 981 } else { 982 throw new KeyStoreException( 983 "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry" 984 + "; was " + entry); 985 } 986 } 987 988 /** 989 * {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from 990 * {@link #getPublicKey()}. This is so that crypto operations on these public keys contain 991 * can find out which keystore private key entry to use. This is needed so that Android Keystore 992 * crypto operations using public keys can find out which key alias to use. These operations 993 * require an alias. 994 */ 995 static class KeyStoreX509Certificate extends DelegatingX509Certificate { 996 private final String mPrivateKeyAlias; 997 private final int mPrivateKeyUid; 998 KeyStoreX509Certificate(String privateKeyAlias, int privateKeyUid, 999 X509Certificate delegate) { 1000 super(delegate); 1001 mPrivateKeyAlias = privateKeyAlias; 1002 mPrivateKeyUid = privateKeyUid; 1003 } 1004 1005 @Override 1006 public PublicKey getPublicKey() { 1007 PublicKey original = super.getPublicKey(); 1008 return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey( 1009 mPrivateKeyAlias, mPrivateKeyUid, 1010 original.getAlgorithm(), original.getEncoded()); 1011 } 1012 } 1013} 1014