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