AndroidKeyStoreSpi.java revision 96481c3ddc6c58cfcad2a5cb9325ee2b24b0c540
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 // TODO: Remove this once keymaster does not require us to specify the size of imported key. 625 args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8); 626 627 if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) 628 && (!params.isRandomizedEncryptionRequired())) { 629 // Permit caller-provided IV when encrypting with this key 630 args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); 631 } 632 633 Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias); 634 String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias; 635 int errorCode = mKeyStore.importKey( 636 keyAliasInKeystore, 637 args, 638 KeymasterDefs.KM_KEY_FORMAT_RAW, 639 keyMaterial, 640 0, // flags 641 new KeyCharacteristics()); 642 if (errorCode != android.security.KeyStore.NO_ERROR) { 643 throw new KeyStoreException("Failed to import secret key. Keystore error code: " 644 + errorCode); 645 } 646 } 647 648 @Override 649 public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain) 650 throws KeyStoreException { 651 throw new KeyStoreException("Operation not supported because key encoding is unknown"); 652 } 653 654 @Override 655 public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException { 656 if (isKeyEntry(alias)) { 657 throw new KeyStoreException("Entry exists and is not a trusted certificate"); 658 } 659 660 // We can't set something to null. 661 if (cert == null) { 662 throw new NullPointerException("cert == null"); 663 } 664 665 final byte[] encoded; 666 try { 667 encoded = cert.getEncoded(); 668 } catch (CertificateEncodingException e) { 669 throw new KeyStoreException(e); 670 } 671 672 if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, 673 android.security.KeyStore.UID_SELF, android.security.KeyStore.FLAG_NONE)) { 674 throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?"); 675 } 676 } 677 678 @Override 679 public void engineDeleteEntry(String alias) throws KeyStoreException { 680 if (!isKeyEntry(alias) && !isCertificateEntry(alias)) { 681 return; 682 } 683 684 if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) { 685 throw new KeyStoreException("No such entry " + alias); 686 } 687 } 688 689 private Set<String> getUniqueAliases() { 690 final String[] rawAliases = mKeyStore.saw(""); 691 if (rawAliases == null) { 692 return new HashSet<String>(); 693 } 694 695 final Set<String> aliases = new HashSet<String>(rawAliases.length); 696 for (String alias : rawAliases) { 697 final int idx = alias.indexOf('_'); 698 if ((idx == -1) || (alias.length() <= idx)) { 699 Log.e(NAME, "invalid alias: " + alias); 700 continue; 701 } 702 703 aliases.add(new String(alias.substring(idx + 1))); 704 } 705 706 return aliases; 707 } 708 709 @Override 710 public Enumeration<String> engineAliases() { 711 return Collections.enumeration(getUniqueAliases()); 712 } 713 714 @Override 715 public boolean engineContainsAlias(String alias) { 716 if (alias == null) { 717 throw new NullPointerException("alias == null"); 718 } 719 720 return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias) 721 || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias) 722 || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias) 723 || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias); 724 } 725 726 @Override 727 public int engineSize() { 728 return getUniqueAliases().size(); 729 } 730 731 @Override 732 public boolean engineIsKeyEntry(String alias) { 733 return isKeyEntry(alias); 734 } 735 736 private boolean isKeyEntry(String alias) { 737 return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias); 738 } 739 740 private boolean isPrivateKeyEntry(String alias) { 741 if (alias == null) { 742 throw new NullPointerException("alias == null"); 743 } 744 745 return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias); 746 } 747 748 private boolean isSecretKeyEntry(String alias) { 749 if (alias == null) { 750 throw new NullPointerException("alias == null"); 751 } 752 753 return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias); 754 } 755 756 private boolean isCertificateEntry(String alias) { 757 if (alias == null) { 758 throw new NullPointerException("alias == null"); 759 } 760 761 return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias); 762 } 763 764 @Override 765 public boolean engineIsCertificateEntry(String alias) { 766 return !isKeyEntry(alias) && isCertificateEntry(alias); 767 } 768 769 @Override 770 public String engineGetCertificateAlias(Certificate cert) { 771 if (cert == null) { 772 return null; 773 } 774 775 final Set<String> nonCaEntries = new HashSet<String>(); 776 777 /* 778 * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation 779 * says to only compare the first certificate in the chain which is 780 * equivalent to the USER_CERTIFICATE prefix for the Android keystore 781 * convention. 782 */ 783 final String[] certAliases = mKeyStore.saw(Credentials.USER_CERTIFICATE); 784 if (certAliases != null) { 785 for (String alias : certAliases) { 786 final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias); 787 if (certBytes == null) { 788 continue; 789 } 790 791 final Certificate c = toCertificate(certBytes); 792 nonCaEntries.add(alias); 793 794 if (cert.equals(c)) { 795 return alias; 796 } 797 } 798 } 799 800 /* 801 * Look at all the TrustedCertificateEntry types. Skip all the 802 * PrivateKeyEntry we looked at above. 803 */ 804 final String[] caAliases = mKeyStore.saw(Credentials.CA_CERTIFICATE); 805 if (certAliases != null) { 806 for (String alias : caAliases) { 807 if (nonCaEntries.contains(alias)) { 808 continue; 809 } 810 811 final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias); 812 if (certBytes == null) { 813 continue; 814 } 815 816 final Certificate c = 817 toCertificate(mKeyStore.get(Credentials.CA_CERTIFICATE + alias)); 818 if (cert.equals(c)) { 819 return alias; 820 } 821 } 822 } 823 824 return null; 825 } 826 827 @Override 828 public void engineStore(OutputStream stream, char[] password) throws IOException, 829 NoSuchAlgorithmException, CertificateException { 830 throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream"); 831 } 832 833 @Override 834 public void engineLoad(InputStream stream, char[] password) throws IOException, 835 NoSuchAlgorithmException, CertificateException { 836 if (stream != null) { 837 throw new IllegalArgumentException("InputStream not supported"); 838 } 839 840 if (password != null) { 841 throw new IllegalArgumentException("password not supported"); 842 } 843 844 // Unfortunate name collision. 845 mKeyStore = android.security.KeyStore.getInstance(); 846 } 847 848 @Override 849 public void engineSetEntry(String alias, Entry entry, ProtectionParameter param) 850 throws KeyStoreException { 851 if (entry == null) { 852 throw new KeyStoreException("entry == null"); 853 } 854 855 if (engineContainsAlias(alias)) { 856 engineDeleteEntry(alias); 857 } 858 859 if (entry instanceof KeyStore.TrustedCertificateEntry) { 860 KeyStore.TrustedCertificateEntry trE = (KeyStore.TrustedCertificateEntry) entry; 861 engineSetCertificateEntry(alias, trE.getTrustedCertificate()); 862 return; 863 } 864 865 if (entry instanceof PrivateKeyEntry) { 866 PrivateKeyEntry prE = (PrivateKeyEntry) entry; 867 setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param); 868 } else if (entry instanceof SecretKeyEntry) { 869 SecretKeyEntry secE = (SecretKeyEntry) entry; 870 setSecretKeyEntry(alias, secE.getSecretKey(), param); 871 } else { 872 throw new KeyStoreException( 873 "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry" 874 + "; was " + entry); 875 } 876 } 877 878} 879