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