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 if (spec.isCriticalToDeviceEncryption()) { 355 flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION; 356 } 357 } else { 358 throw new KeyStoreException( 359 "Unsupported protection parameter class:" + param.getClass().getName() 360 + ". Supported: " + KeyProtection.class.getName() + ", " 361 + KeyStoreParameter.class.getName()); 362 } 363 364 // Make sure the chain exists since this is a PrivateKey 365 if ((chain == null) || (chain.length == 0)) { 366 throw new KeyStoreException("Must supply at least one Certificate with PrivateKey"); 367 } 368 369 // Do chain type checking. 370 X509Certificate[] x509chain = new X509Certificate[chain.length]; 371 for (int i = 0; i < chain.length; i++) { 372 if (!"X.509".equals(chain[i].getType())) { 373 throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #" 374 + i); 375 } 376 377 if (!(chain[i] instanceof X509Certificate)) { 378 throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #" 379 + i); 380 } 381 382 x509chain[i] = (X509Certificate) chain[i]; 383 } 384 385 final byte[] userCertBytes; 386 try { 387 userCertBytes = x509chain[0].getEncoded(); 388 } catch (CertificateEncodingException e) { 389 throw new KeyStoreException("Failed to encode certificate #0", e); 390 } 391 392 /* 393 * If we have a chain, store it in the CA certificate slot for this 394 * alias as concatenated DER-encoded certificates. These can be 395 * deserialized by {@link CertificateFactory#generateCertificates}. 396 */ 397 final byte[] chainBytes; 398 if (chain.length > 1) { 399 /* 400 * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...} 401 * so we only need the certificates starting at index 1. 402 */ 403 final byte[][] certsBytes = new byte[x509chain.length - 1][]; 404 int totalCertLength = 0; 405 for (int i = 0; i < certsBytes.length; i++) { 406 try { 407 certsBytes[i] = x509chain[i + 1].getEncoded(); 408 totalCertLength += certsBytes[i].length; 409 } catch (CertificateEncodingException e) { 410 throw new KeyStoreException("Failed to encode certificate #" + i, e); 411 } 412 } 413 414 /* 415 * Serialize this into one byte array so we can later call 416 * CertificateFactory#generateCertificates to recover them. 417 */ 418 chainBytes = new byte[totalCertLength]; 419 int outputOffset = 0; 420 for (int i = 0; i < certsBytes.length; i++) { 421 final int certLength = certsBytes[i].length; 422 System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength); 423 outputOffset += certLength; 424 certsBytes[i] = null; 425 } 426 } else { 427 chainBytes = null; 428 } 429 430 final String pkeyAlias; 431 if (key instanceof AndroidKeyStorePrivateKey) { 432 pkeyAlias = ((AndroidKeyStoreKey) key).getAlias(); 433 } else { 434 pkeyAlias = null; 435 } 436 437 byte[] pkcs8EncodedPrivateKeyBytes; 438 KeymasterArguments importArgs; 439 final boolean shouldReplacePrivateKey; 440 if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) { 441 final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length()); 442 if (!alias.equals(keySubalias)) { 443 throw new KeyStoreException("Can only replace keys with same alias: " + alias 444 + " != " + keySubalias); 445 } 446 shouldReplacePrivateKey = false; 447 importArgs = null; 448 pkcs8EncodedPrivateKeyBytes = null; 449 } else { 450 shouldReplacePrivateKey = true; 451 // Make sure the PrivateKey format is the one we support. 452 final String keyFormat = key.getFormat(); 453 if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) { 454 throw new KeyStoreException( 455 "Unsupported private key export format: " + keyFormat 456 + ". Only private keys which export their key material in PKCS#8 format are" 457 + " supported."); 458 } 459 460 // Make sure we can actually encode the key. 461 pkcs8EncodedPrivateKeyBytes = key.getEncoded(); 462 if (pkcs8EncodedPrivateKeyBytes == null) { 463 throw new KeyStoreException("Private key did not export any key material"); 464 } 465 466 importArgs = new KeymasterArguments(); 467 try { 468 importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, 469 KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm( 470 key.getAlgorithm())); 471 @KeyProperties.PurposeEnum int purposes = spec.getPurposes(); 472 importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE, 473 KeyProperties.Purpose.allToKeymaster(purposes)); 474 if (spec.isDigestsSpecified()) { 475 importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST, 476 KeyProperties.Digest.allToKeymaster(spec.getDigests())); 477 } 478 479 importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, 480 KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes())); 481 int[] keymasterEncryptionPaddings = 482 KeyProperties.EncryptionPadding.allToKeymaster( 483 spec.getEncryptionPaddings()); 484 if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) 485 && (spec.isRandomizedEncryptionRequired())) { 486 for (int keymasterPadding : keymasterEncryptionPaddings) { 487 if (!KeymasterUtils 488 .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( 489 keymasterPadding)) { 490 throw new KeyStoreException( 491 "Randomized encryption (IND-CPA) required but is violated by" 492 + " encryption padding mode: " 493 + KeyProperties.EncryptionPadding.fromKeymaster( 494 keymasterPadding) 495 + ". See KeyProtection documentation."); 496 } 497 } 498 } 499 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings); 500 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, 501 KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings())); 502 KeymasterUtils.addUserAuthArgs(importArgs, 503 spec.isUserAuthenticationRequired(), 504 spec.getUserAuthenticationValidityDurationSeconds(), 505 spec.isUserAuthenticationValidWhileOnBody(), 506 spec.isInvalidatedByBiometricEnrollment(), 507 spec.getBoundToSpecificSecureUserId()); 508 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, 509 spec.getKeyValidityStart()); 510 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, 511 spec.getKeyValidityForOriginationEnd()); 512 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, 513 spec.getKeyValidityForConsumptionEnd()); 514 } catch (IllegalArgumentException | IllegalStateException e) { 515 throw new KeyStoreException(e); 516 } 517 } 518 519 520 boolean success = false; 521 try { 522 // Store the private key, if necessary 523 if (shouldReplacePrivateKey) { 524 // Delete the stored private key and any related entries before importing the 525 // provided key 526 Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid); 527 KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics(); 528 int errorCode = mKeyStore.importKey( 529 Credentials.USER_PRIVATE_KEY + alias, 530 importArgs, 531 KeymasterDefs.KM_KEY_FORMAT_PKCS8, 532 pkcs8EncodedPrivateKeyBytes, 533 mUid, 534 flags, 535 resultingKeyCharacteristics); 536 if (errorCode != KeyStore.NO_ERROR) { 537 throw new KeyStoreException("Failed to store private key", 538 KeyStore.getKeyStoreException(errorCode)); 539 } 540 } else { 541 // Keep the stored private key around -- delete all other entry types 542 Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid); 543 Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid); 544 } 545 546 // Store the leaf certificate 547 int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes, 548 mUid, flags); 549 if (errorCode != KeyStore.NO_ERROR) { 550 throw new KeyStoreException("Failed to store certificate #0", 551 KeyStore.getKeyStoreException(errorCode)); 552 } 553 554 // Store the certificate chain 555 errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes, 556 mUid, flags); 557 if (errorCode != KeyStore.NO_ERROR) { 558 throw new KeyStoreException("Failed to store certificate chain", 559 KeyStore.getKeyStoreException(errorCode)); 560 } 561 success = true; 562 } finally { 563 if (!success) { 564 if (shouldReplacePrivateKey) { 565 Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid); 566 } else { 567 Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid); 568 Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid); 569 } 570 } 571 } 572 } 573 574 private void setSecretKeyEntry(String entryAlias, SecretKey key, 575 java.security.KeyStore.ProtectionParameter param) 576 throws KeyStoreException { 577 if ((param != null) && (!(param instanceof KeyProtection))) { 578 throw new KeyStoreException( 579 "Unsupported protection parameter class: " + param.getClass().getName() 580 + ". Supported: " + KeyProtection.class.getName()); 581 } 582 KeyProtection params = (KeyProtection) param; 583 584 if (key instanceof AndroidKeyStoreSecretKey) { 585 // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot 586 // overwrite its own entry. 587 String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias(); 588 if (keyAliasInKeystore == null) { 589 throw new KeyStoreException("KeyStore-backed secret key does not have an alias"); 590 } 591 if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) { 592 throw new KeyStoreException("KeyStore-backed secret key has invalid alias: " 593 + keyAliasInKeystore); 594 } 595 String keyEntryAlias = 596 keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length()); 597 if (!entryAlias.equals(keyEntryAlias)) { 598 throw new KeyStoreException("Can only replace KeyStore-backed keys with same" 599 + " alias: " + entryAlias + " != " + keyEntryAlias); 600 } 601 // This is the entry where this key is already stored. No need to do anything. 602 if (params != null) { 603 throw new KeyStoreException("Modifying KeyStore-backed key using protection" 604 + " parameters not supported"); 605 } 606 return; 607 } 608 609 if (params == null) { 610 throw new KeyStoreException( 611 "Protection parameters must be specified when importing a symmetric key"); 612 } 613 614 // Not a KeyStore-backed secret key -- import its key material into keystore. 615 String keyExportFormat = key.getFormat(); 616 if (keyExportFormat == null) { 617 throw new KeyStoreException( 618 "Only secret keys that export their key material are supported"); 619 } else if (!"RAW".equals(keyExportFormat)) { 620 throw new KeyStoreException( 621 "Unsupported secret key material export format: " + keyExportFormat); 622 } 623 byte[] keyMaterial = key.getEncoded(); 624 if (keyMaterial == null) { 625 throw new KeyStoreException("Key did not export its key material despite supporting" 626 + " RAW format export"); 627 } 628 629 KeymasterArguments args = new KeymasterArguments(); 630 try { 631 int keymasterAlgorithm = 632 KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(key.getAlgorithm()); 633 args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm); 634 635 int[] keymasterDigests; 636 if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { 637 // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm 638 // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one 639 // digest, we don't let import parameters override the digest implied by the key. 640 // If the parameters specify digests at all, they must specify only one digest, the 641 // only implied by key algorithm. 642 int keymasterImpliedDigest = 643 KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm()); 644 if (keymasterImpliedDigest == -1) { 645 throw new ProviderException( 646 "HMAC key algorithm digest unknown for key algorithm " 647 + key.getAlgorithm()); 648 } 649 keymasterDigests = new int[] {keymasterImpliedDigest}; 650 if (params.isDigestsSpecified()) { 651 // Digest(s) explicitly specified in params -- check that the list consists of 652 // exactly one digest, the one implied by key algorithm. 653 int[] keymasterDigestsFromParams = 654 KeyProperties.Digest.allToKeymaster(params.getDigests()); 655 if ((keymasterDigestsFromParams.length != 1) 656 || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) { 657 throw new KeyStoreException( 658 "Unsupported digests specification: " 659 + Arrays.asList(params.getDigests()) + ". Only " 660 + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest) 661 + " supported for HMAC key algorithm " + key.getAlgorithm()); 662 } 663 } 664 } else { 665 // Key algorithm does not imply a digest. 666 if (params.isDigestsSpecified()) { 667 keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests()); 668 } else { 669 keymasterDigests = EmptyArray.INT; 670 } 671 } 672 args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests); 673 674 @KeyProperties.PurposeEnum int purposes = params.getPurposes(); 675 int[] keymasterBlockModes = 676 KeyProperties.BlockMode.allToKeymaster(params.getBlockModes()); 677 if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) 678 && (params.isRandomizedEncryptionRequired())) { 679 for (int keymasterBlockMode : keymasterBlockModes) { 680 if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto( 681 keymasterBlockMode)) { 682 throw new KeyStoreException( 683 "Randomized encryption (IND-CPA) required but may be violated by" 684 + " block mode: " 685 + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode) 686 + ". See KeyProtection documentation."); 687 } 688 } 689 } 690 args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, 691 KeyProperties.Purpose.allToKeymaster(purposes)); 692 args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes); 693 if (params.getSignaturePaddings().length > 0) { 694 throw new KeyStoreException("Signature paddings not supported for symmetric keys"); 695 } 696 int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster( 697 params.getEncryptionPaddings()); 698 args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings); 699 KeymasterUtils.addUserAuthArgs(args, 700 params.isUserAuthenticationRequired(), 701 params.getUserAuthenticationValidityDurationSeconds(), 702 params.isUserAuthenticationValidWhileOnBody(), 703 params.isInvalidatedByBiometricEnrollment(), 704 params.getBoundToSpecificSecureUserId()); 705 KeymasterUtils.addMinMacLengthAuthorizationIfNecessary( 706 args, 707 keymasterAlgorithm, 708 keymasterBlockModes, 709 keymasterDigests); 710 args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, 711 params.getKeyValidityStart()); 712 args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, 713 params.getKeyValidityForOriginationEnd()); 714 args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, 715 params.getKeyValidityForConsumptionEnd()); 716 717 if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) 718 && (!params.isRandomizedEncryptionRequired())) { 719 // Permit caller-provided IV when encrypting with this key 720 args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); 721 } 722 } catch (IllegalArgumentException | IllegalStateException e) { 723 throw new KeyStoreException(e); 724 } 725 int flags = 0; 726 if (params.isCriticalToDeviceEncryption()) { 727 flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION; 728 } 729 730 Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid); 731 String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias; 732 int errorCode = mKeyStore.importKey( 733 keyAliasInKeystore, 734 args, 735 KeymasterDefs.KM_KEY_FORMAT_RAW, 736 keyMaterial, 737 mUid, 738 flags, 739 new KeyCharacteristics()); 740 if (errorCode != KeyStore.NO_ERROR) { 741 throw new KeyStoreException("Failed to import secret key. Keystore error code: " 742 + errorCode); 743 } 744 } 745 746 @Override 747 public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain) 748 throws KeyStoreException { 749 throw new KeyStoreException("Operation not supported because key encoding is unknown"); 750 } 751 752 @Override 753 public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException { 754 if (isKeyEntry(alias)) { 755 throw new KeyStoreException("Entry exists and is not a trusted certificate"); 756 } 757 758 // We can't set something to null. 759 if (cert == null) { 760 throw new NullPointerException("cert == null"); 761 } 762 763 final byte[] encoded; 764 try { 765 encoded = cert.getEncoded(); 766 } catch (CertificateEncodingException e) { 767 throw new KeyStoreException(e); 768 } 769 770 if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, mUid, KeyStore.FLAG_NONE)) { 771 throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?"); 772 } 773 } 774 775 @Override 776 public void engineDeleteEntry(String alias) throws KeyStoreException { 777 if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid)) { 778 throw new KeyStoreException("Failed to delete entry: " + alias); 779 } 780 } 781 782 private Set<String> getUniqueAliases() { 783 final String[] rawAliases = mKeyStore.list("", mUid); 784 if (rawAliases == null) { 785 return new HashSet<String>(); 786 } 787 788 final Set<String> aliases = new HashSet<String>(rawAliases.length); 789 for (String alias : rawAliases) { 790 final int idx = alias.indexOf('_'); 791 if ((idx == -1) || (alias.length() <= idx)) { 792 Log.e(NAME, "invalid alias: " + alias); 793 continue; 794 } 795 796 aliases.add(new String(alias.substring(idx + 1))); 797 } 798 799 return aliases; 800 } 801 802 @Override 803 public Enumeration<String> engineAliases() { 804 return Collections.enumeration(getUniqueAliases()); 805 } 806 807 @Override 808 public boolean engineContainsAlias(String alias) { 809 if (alias == null) { 810 throw new NullPointerException("alias == null"); 811 } 812 813 return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid) 814 || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid) 815 || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias, mUid) 816 || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid); 817 } 818 819 @Override 820 public int engineSize() { 821 return getUniqueAliases().size(); 822 } 823 824 @Override 825 public boolean engineIsKeyEntry(String alias) { 826 return isKeyEntry(alias); 827 } 828 829 private boolean isKeyEntry(String alias) { 830 return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias); 831 } 832 833 private boolean isPrivateKeyEntry(String alias) { 834 if (alias == null) { 835 throw new NullPointerException("alias == null"); 836 } 837 838 return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid); 839 } 840 841 private boolean isSecretKeyEntry(String alias) { 842 if (alias == null) { 843 throw new NullPointerException("alias == null"); 844 } 845 846 return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid); 847 } 848 849 private boolean isCertificateEntry(String alias) { 850 if (alias == null) { 851 throw new NullPointerException("alias == null"); 852 } 853 854 return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid); 855 } 856 857 @Override 858 public boolean engineIsCertificateEntry(String alias) { 859 return !isKeyEntry(alias) && isCertificateEntry(alias); 860 } 861 862 @Override 863 public String engineGetCertificateAlias(Certificate cert) { 864 if (cert == null) { 865 return null; 866 } 867 if (!"X.509".equalsIgnoreCase(cert.getType())) { 868 // Only X.509 certificates supported 869 return null; 870 } 871 byte[] targetCertBytes; 872 try { 873 targetCertBytes = cert.getEncoded(); 874 } catch (CertificateEncodingException e) { 875 return null; 876 } 877 if (targetCertBytes == null) { 878 return null; 879 } 880 881 final Set<String> nonCaEntries = new HashSet<String>(); 882 883 /* 884 * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation 885 * says to only compare the first certificate in the chain which is 886 * equivalent to the USER_CERTIFICATE prefix for the Android keystore 887 * convention. 888 */ 889 final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE, mUid); 890 if (certAliases != null) { 891 for (String alias : certAliases) { 892 final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid); 893 if (certBytes == null) { 894 continue; 895 } 896 897 nonCaEntries.add(alias); 898 899 if (Arrays.equals(certBytes, targetCertBytes)) { 900 return alias; 901 } 902 } 903 } 904 905 /* 906 * Look at all the TrustedCertificateEntry types. Skip all the 907 * PrivateKeyEntry we looked at above. 908 */ 909 final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE, mUid); 910 if (certAliases != null) { 911 for (String alias : caAliases) { 912 if (nonCaEntries.contains(alias)) { 913 continue; 914 } 915 916 final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid); 917 if (certBytes == null) { 918 continue; 919 } 920 921 if (Arrays.equals(certBytes, targetCertBytes)) { 922 return alias; 923 } 924 } 925 } 926 927 return null; 928 } 929 930 @Override 931 public void engineStore(OutputStream stream, char[] password) throws IOException, 932 NoSuchAlgorithmException, CertificateException { 933 throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream"); 934 } 935 936 @Override 937 public void engineLoad(InputStream stream, char[] password) throws IOException, 938 NoSuchAlgorithmException, CertificateException { 939 if (stream != null) { 940 throw new IllegalArgumentException("InputStream not supported"); 941 } 942 943 if (password != null) { 944 throw new IllegalArgumentException("password not supported"); 945 } 946 947 // Unfortunate name collision. 948 mKeyStore = KeyStore.getInstance(); 949 mUid = KeyStore.UID_SELF; 950 } 951 952 @Override 953 public void engineLoad(LoadStoreParameter param) throws IOException, 954 NoSuchAlgorithmException, CertificateException { 955 int uid = KeyStore.UID_SELF; 956 if (param != null) { 957 if (param instanceof AndroidKeyStoreLoadStoreParameter) { 958 uid = ((AndroidKeyStoreLoadStoreParameter) param).getUid(); 959 } else { 960 throw new IllegalArgumentException( 961 "Unsupported param type: " + param.getClass()); 962 } 963 } 964 mKeyStore = KeyStore.getInstance(); 965 mUid = uid; 966 } 967 968 @Override 969 public void engineSetEntry(String alias, Entry entry, ProtectionParameter param) 970 throws KeyStoreException { 971 if (entry == null) { 972 throw new KeyStoreException("entry == null"); 973 } 974 975 Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid); 976 977 if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) { 978 java.security.KeyStore.TrustedCertificateEntry trE = 979 (java.security.KeyStore.TrustedCertificateEntry) entry; 980 engineSetCertificateEntry(alias, trE.getTrustedCertificate()); 981 return; 982 } 983 984 if (entry instanceof PrivateKeyEntry) { 985 PrivateKeyEntry prE = (PrivateKeyEntry) entry; 986 setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param); 987 } else if (entry instanceof SecretKeyEntry) { 988 SecretKeyEntry secE = (SecretKeyEntry) entry; 989 setSecretKeyEntry(alias, secE.getSecretKey(), param); 990 } else { 991 throw new KeyStoreException( 992 "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry" 993 + "; was " + entry); 994 } 995 } 996 997 /** 998 * {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from 999 * {@link #getPublicKey()}. This is so that crypto operations on these public keys contain 1000 * can find out which keystore private key entry to use. This is needed so that Android Keystore 1001 * crypto operations using public keys can find out which key alias to use. These operations 1002 * require an alias. 1003 */ 1004 static class KeyStoreX509Certificate extends DelegatingX509Certificate { 1005 private final String mPrivateKeyAlias; 1006 private final int mPrivateKeyUid; 1007 KeyStoreX509Certificate(String privateKeyAlias, int privateKeyUid, 1008 X509Certificate delegate) { 1009 super(delegate); 1010 mPrivateKeyAlias = privateKeyAlias; 1011 mPrivateKeyUid = privateKeyUid; 1012 } 1013 1014 @Override 1015 public PublicKey getPublicKey() { 1016 PublicKey original = super.getPublicKey(); 1017 return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey( 1018 mPrivateKeyAlias, mPrivateKeyUid, 1019 original.getAlgorithm(), original.getEncoded()); 1020 } 1021 } 1022} 1023