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