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