1 /* 2 * Copyright (C) 2011 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 package android.security; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.annotation.SdkConstant; 21 import android.annotation.WorkerThread; 22 import android.annotation.SdkConstant.SdkConstantType; 23 import android.app.Activity; 24 import android.app.PendingIntent; 25 import android.app.Service; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.ServiceConnection; 30 import android.net.Uri; 31 import android.os.Binder; 32 import android.os.Build; 33 import android.os.IBinder; 34 import android.os.Looper; 35 import android.os.Process; 36 import android.os.RemoteException; 37 import android.os.UserHandle; 38 import android.security.keystore.AndroidKeyStoreProvider; 39 import android.security.keystore.KeyProperties; 40 41 import java.io.ByteArrayInputStream; 42 import java.io.Closeable; 43 import java.security.KeyPair; 44 import java.security.Principal; 45 import java.security.PrivateKey; 46 import java.security.UnrecoverableKeyException; 47 import java.security.cert.Certificate; 48 import java.security.cert.CertificateException; 49 import java.security.cert.CertificateFactory; 50 import java.security.cert.X509Certificate; 51 import java.util.ArrayList; 52 import java.util.Collection; 53 import java.util.List; 54 import java.util.Locale; 55 import java.util.concurrent.BlockingQueue; 56 import java.util.concurrent.LinkedBlockingQueue; 57 58 import com.android.org.conscrypt.TrustedCertificateStore; 59 60 /** 61 * The {@code KeyChain} class provides access to private keys and 62 * their corresponding certificate chains in credential storage. 63 * 64 * <p>Applications accessing the {@code KeyChain} normally go through 65 * these steps: 66 * 67 * <ol> 68 * 69 * <li>Receive a callback from an {@link javax.net.ssl.X509KeyManager 70 * X509KeyManager} that a private key is requested. 71 * 72 * <li>Call {@link #choosePrivateKeyAlias 73 * choosePrivateKeyAlias} to allow the user to select from a 74 * list of currently available private keys and corresponding 75 * certificate chains. The chosen alias will be returned by the 76 * callback {@link KeyChainAliasCallback#alias}, or null if no private 77 * key is available or the user cancels the request. 78 * 79 * <li>Call {@link #getPrivateKey} and {@link #getCertificateChain} to 80 * retrieve the credentials to return to the corresponding {@link 81 * javax.net.ssl.X509KeyManager} callbacks. 82 * 83 * </ol> 84 * 85 * <p>An application may remember the value of a selected alias to 86 * avoid prompting the user with {@link #choosePrivateKeyAlias 87 * choosePrivateKeyAlias} on subsequent connections. If the alias is 88 * no longer valid, null will be returned on lookups using that value 89 * 90 * <p>An application can request the installation of private keys and 91 * certificates via the {@code Intent} provided by {@link 92 * #createInstallIntent}. Private keys installed via this {@code 93 * Intent} will be accessible via {@link #choosePrivateKeyAlias} while 94 * Certificate Authority (CA) certificates will be trusted by all 95 * applications through the default {@code X509TrustManager}. 96 */ 97 // TODO reference intent for credential installation when public 98 public final class KeyChain { 99 100 /** 101 * @hide Also used by KeyChainService implementation 102 */ 103 public static final String ACCOUNT_TYPE = "com.android.keychain"; 104 105 /** 106 * Package name for KeyChain chooser. 107 */ 108 private static final String KEYCHAIN_PACKAGE = "com.android.keychain"; 109 110 /** 111 * Action to bring up the KeyChainActivity 112 */ 113 private static final String ACTION_CHOOSER = "com.android.keychain.CHOOSER"; 114 115 /** 116 * Package name for the Certificate Installer. 117 */ 118 private static final String CERT_INSTALLER_PACKAGE = "com.android.certinstaller"; 119 120 /** 121 * Extra for use with {@link #ACTION_CHOOSER} 122 * @hide Also used by KeyChainActivity implementation 123 */ 124 public static final String EXTRA_RESPONSE = "response"; 125 126 /** 127 * Extra for use with {@link #ACTION_CHOOSER} 128 * @hide Also used by KeyChainActivity implementation 129 */ 130 public static final String EXTRA_URI = "uri"; 131 132 /** 133 * Extra for use with {@link #ACTION_CHOOSER} 134 * @hide Also used by KeyChainActivity implementation 135 */ 136 public static final String EXTRA_ALIAS = "alias"; 137 138 /** 139 * Extra for use with {@link #ACTION_CHOOSER} 140 * @hide Also used by KeyChainActivity implementation 141 */ 142 public static final String EXTRA_SENDER = "sender"; 143 144 /** 145 * Action to bring up the CertInstaller. 146 */ 147 private static final String ACTION_INSTALL = "android.credentials.INSTALL"; 148 149 /** 150 * Optional extra to specify a {@code String} credential name on 151 * the {@code Intent} returned by {@link #createInstallIntent}. 152 */ 153 // Compatible with old com.android.certinstaller.CredentialHelper.CERT_NAME_KEY 154 public static final String EXTRA_NAME = "name"; 155 156 /** 157 * Optional extra to specify an X.509 certificate to install on 158 * the {@code Intent} returned by {@link #createInstallIntent}. 159 * The extra value should be a PEM or ASN.1 DER encoded {@code 160 * byte[]}. An {@link X509Certificate} can be converted to DER 161 * encoded bytes with {@link X509Certificate#getEncoded}. 162 * 163 * <p>{@link #EXTRA_NAME} may be used to provide a default alias 164 * name for the installed certificate. 165 */ 166 // Compatible with old android.security.Credentials.CERTIFICATE 167 public static final String EXTRA_CERTIFICATE = "CERT"; 168 169 /** 170 * Optional extra for use with the {@code Intent} returned by 171 * {@link #createInstallIntent} to specify a PKCS#12 key store to 172 * install. The extra value should be a {@code byte[]}. The bytes 173 * may come from an external source or be generated with {@link 174 * java.security.KeyStore#store} on a "PKCS12" instance. 175 * 176 * <p>The user will be prompted for the password to load the key store. 177 * 178 * <p>The key store will be scanned for {@link 179 * java.security.KeyStore.PrivateKeyEntry} entries and both the 180 * private key and associated certificate chain will be installed. 181 * 182 * <p>{@link #EXTRA_NAME} may be used to provide a default alias 183 * name for the installed credentials. 184 */ 185 // Compatible with old android.security.Credentials.PKCS12 186 public static final String EXTRA_PKCS12 = "PKCS12"; 187 188 /** 189 * Broadcast Action: Indicates the trusted storage has changed. Sent when 190 * one of this happens: 191 * 192 * <ul> 193 * <li>a new CA is added, 194 * <li>an existing CA is removed or disabled, 195 * <li>a disabled CA is enabled, 196 * <li>trusted storage is reset (all user certs are cleared), 197 * <li>when permission to access a private key is changed. 198 * </ul> 199 * 200 * @deprecated Use {@link #ACTION_KEYCHAIN_CHANGED}, {@link #ACTION_TRUST_STORE_CHANGED} or 201 * {@link #ACTION_KEY_ACCESS_CHANGED}. Apps that target a version higher than 202 * {@link Build.VERSION_CODES#N_MR1} will only receive this broadcast if they register for it 203 * at runtime. 204 */ 205 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 206 public static final String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED"; 207 208 /** 209 * Broadcast Action: Indicates the contents of the keychain has changed. Sent when a KeyChain 210 * entry is added, modified or removed. 211 */ 212 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 213 public static final String ACTION_KEYCHAIN_CHANGED = "android.security.action.KEYCHAIN_CHANGED"; 214 215 /** 216 * Broadcast Action: Indicates the contents of the trusted certificate store has changed. Sent 217 * when one the following occurs: 218 * 219 * <ul> 220 * <li>A pre-installed CA is disabled or re-enabled</li> 221 * <li>A CA is added or removed from the trust store</li> 222 * </ul> 223 */ 224 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 225 public static final String ACTION_TRUST_STORE_CHANGED = 226 "android.security.action.TRUST_STORE_CHANGED"; 227 228 /** 229 * Broadcast Action: Indicates that the access permissions for a private key have changed. 230 * 231 */ 232 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 233 public static final String ACTION_KEY_ACCESS_CHANGED = 234 "android.security.action.KEY_ACCESS_CHANGED"; 235 236 /** 237 * Used as a String extra field in {@link #ACTION_KEY_ACCESS_CHANGED} to supply the alias of 238 * the key. 239 */ 240 public static final String EXTRA_KEY_ALIAS = "android.security.extra.KEY_ALIAS"; 241 242 /** 243 * Used as a boolean extra field in {@link #ACTION_KEY_ACCESS_CHANGED} to supply if the key is 244 * accessible to the application. 245 */ 246 public static final String EXTRA_KEY_ACCESSIBLE = "android.security.extra.KEY_ACCESSIBLE"; 247 248 /** 249 * Indicates that a call to {@link #generateKeyPair} was successful. 250 * @hide 251 */ 252 public static final int KEY_GEN_SUCCESS = 0; 253 254 /** 255 * An alias was missing from the key specifications when calling {@link #generateKeyPair}. 256 * @hide 257 */ 258 public static final int KEY_GEN_MISSING_ALIAS = 1; 259 260 /** 261 * A key attestation challenge was provided to {@link #generateKeyPair}, but it shouldn't 262 * have been provided. 263 * @hide 264 */ 265 public static final int KEY_GEN_SUPERFLUOUS_ATTESTATION_CHALLENGE = 2; 266 267 /** 268 * Algorithm not supported by {@link #generateKeyPair} 269 * @hide 270 */ 271 public static final int KEY_GEN_NO_SUCH_ALGORITHM = 3; 272 273 /** 274 * Invalid algorithm parameters when calling {@link #generateKeyPair} 275 * @hide 276 */ 277 public static final int KEY_GEN_INVALID_ALGORITHM_PARAMETERS = 4; 278 279 /** 280 * Keystore is not available when calling {@link #generateKeyPair} 281 * @hide 282 */ 283 public static final int KEY_GEN_NO_KEYSTORE_PROVIDER = 5; 284 285 /** 286 * General failure while calling {@link #generateKeyPair} 287 * @hide 288 */ 289 public static final int KEY_GEN_FAILURE = 6; 290 291 /** 292 * Successful call to {@link #attestKey} 293 * @hide 294 */ 295 public static final int KEY_ATTESTATION_SUCCESS = 0; 296 297 /** 298 * Attestation challenge missing when calling {@link #attestKey} 299 * @hide 300 */ 301 public static final int KEY_ATTESTATION_MISSING_CHALLENGE = 1; 302 303 /** 304 * The caller requested Device ID attestation when calling {@link #attestKey}, but has no 305 * permissions to get device identifiers. 306 * @hide 307 */ 308 public static final int KEY_ATTESTATION_CANNOT_COLLECT_DATA = 2; 309 310 /** 311 * The underlying hardware does not support Device ID attestation or cannot attest to the 312 * identifiers that are stored on the device. This indicates permanent inability 313 * to get attestation records on the device. 314 * @hide 315 */ 316 public static final int KEY_ATTESTATION_CANNOT_ATTEST_IDS = 3; 317 318 /** 319 * General failure when calling {@link #attestKey} 320 * @hide 321 */ 322 public static final int KEY_ATTESTATION_FAILURE = 4; 323 324 /** 325 * Returns an {@code Intent} that can be used for credential 326 * installation. The intent may be used without any extras, in 327 * which case the user will be able to install credentials from 328 * their own source. 329 * 330 * <p>Alternatively, {@link #EXTRA_CERTIFICATE} or {@link 331 * #EXTRA_PKCS12} maybe used to specify the bytes of an X.509 332 * certificate or a PKCS#12 key store for installation. These 333 * extras may be combined with {@link #EXTRA_NAME} to provide a 334 * default alias name for credentials being installed. 335 * 336 * <p>When used with {@link Activity#startActivityForResult}, 337 * {@link Activity#RESULT_OK} will be returned if a credential was 338 * successfully installed, otherwise {@link 339 * Activity#RESULT_CANCELED} will be returned. 340 */ 341 @NonNull createInstallIntent()342 public static Intent createInstallIntent() { 343 Intent intent = new Intent(ACTION_INSTALL); 344 intent.setClassName(CERT_INSTALLER_PACKAGE, 345 "com.android.certinstaller.CertInstallerMain"); 346 return intent; 347 } 348 349 /** 350 * Launches an {@code Activity} for the user to select the alias 351 * for a private key and certificate pair for authentication. The 352 * selected alias or null will be returned via the 353 * KeyChainAliasCallback callback. 354 * 355 * <p>A device policy controller (as a device or profile owner) can 356 * intercept the request before the activity is shown, to pick a 357 * specific private key alias by implementing 358 * {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias 359 * onChoosePrivateKeyAlias}. 360 * 361 * <p>{@code keyTypes} and {@code issuers} may be used to 362 * highlight suggested choices to the user, although to cope with 363 * sometimes erroneous values provided by servers, the user may be 364 * able to override these suggestions. 365 * 366 * <p>{@code host} and {@code port} may be used to give the user 367 * more context about the server requesting the credentials. 368 * 369 * <p>{@code alias} allows the caller to preselect an existing 370 * alias which will still be subject to user confirmation. 371 * 372 * @param activity The {@link Activity} context to use for 373 * launching the new sub-Activity to prompt the user to select 374 * a private key; used only to call startActivity(); must not 375 * be null. 376 * @param response Callback to invoke when the request completes; 377 * must not be null. 378 * @param keyTypes The acceptable types of asymmetric keys such as 379 * "RSA" or "DSA", or null. 380 * @param issuers The acceptable certificate issuers for the 381 * certificate matching the private key, or null. 382 * @param host The host name of the server requesting the 383 * certificate, or null if unavailable. 384 * @param port The port number of the server requesting the 385 * certificate, or -1 if unavailable. 386 * @param alias The alias to preselect if available, or null if 387 * unavailable. 388 */ choosePrivateKeyAlias(@onNull Activity activity, @NonNull KeyChainAliasCallback response, @Nullable @KeyProperties.KeyAlgorithmEnum String[] keyTypes, @Nullable Principal[] issuers, @Nullable String host, int port, @Nullable String alias)389 public static void choosePrivateKeyAlias(@NonNull Activity activity, 390 @NonNull KeyChainAliasCallback response, 391 @Nullable @KeyProperties.KeyAlgorithmEnum String[] keyTypes, 392 @Nullable Principal[] issuers, 393 @Nullable String host, int port, @Nullable String alias) { 394 Uri uri = null; 395 if (host != null) { 396 uri = new Uri.Builder() 397 .authority(host + (port != -1 ? ":" + port : "")) 398 .build(); 399 } 400 choosePrivateKeyAlias(activity, response, keyTypes, issuers, uri, alias); 401 } 402 403 /** 404 * Launches an {@code Activity} for the user to select the alias 405 * for a private key and certificate pair for authentication. The 406 * selected alias or null will be returned via the 407 * KeyChainAliasCallback callback. 408 * 409 * <p>A device policy controller (as a device or profile owner) can 410 * intercept the request before the activity is shown, to pick a 411 * specific private key alias by implementing 412 * {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias 413 * onChoosePrivateKeyAlias}. 414 * 415 * <p>{@code keyTypes} and {@code issuers} may be used to 416 * highlight suggested choices to the user, although to cope with 417 * sometimes erroneous values provided by servers, the user may be 418 * able to override these suggestions. 419 * 420 * <p>{@code uri} may be used to give the user more context about 421 * the server requesting the credentials. 422 * 423 * <p>{@code alias} allows the caller to preselect an existing 424 * alias which will still be subject to user confirmation. 425 * 426 * @param activity The {@link Activity} context to use for 427 * launching the new sub-Activity to prompt the user to select 428 * a private key; used only to call startActivity(); must not 429 * be null. 430 * @param response Callback to invoke when the request completes; 431 * must not be null. 432 * @param keyTypes The acceptable types of asymmetric keys such as 433 * "EC" or "RSA", or null. 434 * @param issuers The acceptable certificate issuers for the 435 * certificate matching the private key, or null. 436 * @param uri The full URI the server is requesting the certificate 437 * for, or null if unavailable. 438 * @param alias The alias to preselect if available, or null if 439 * unavailable. 440 */ choosePrivateKeyAlias(@onNull Activity activity, @NonNull KeyChainAliasCallback response, @Nullable @KeyProperties.KeyAlgorithmEnum String[] keyTypes, @Nullable Principal[] issuers, @Nullable Uri uri, @Nullable String alias)441 public static void choosePrivateKeyAlias(@NonNull Activity activity, 442 @NonNull KeyChainAliasCallback response, 443 @Nullable @KeyProperties.KeyAlgorithmEnum String[] keyTypes, 444 @Nullable Principal[] issuers, 445 @Nullable Uri uri, @Nullable String alias) { 446 /* 447 * TODO currently keyTypes, issuers are unused. They are meant 448 * to follow the semantics and purpose of X509KeyManager 449 * method arguments. 450 * 451 * keyTypes would allow the list to be filtered and typically 452 * will be set correctly by the server. In practice today, 453 * most all users will want only RSA or EC, and usually 454 * only a small number of certs will be available. 455 * 456 * issuers is typically not useful. Some servers historically 457 * will send the entire list of public CAs known to the 458 * server. Others will send none. If this is used, if there 459 * are no matches after applying the constraint, it should be 460 * ignored. 461 */ 462 if (activity == null) { 463 throw new NullPointerException("activity == null"); 464 } 465 if (response == null) { 466 throw new NullPointerException("response == null"); 467 } 468 Intent intent = new Intent(ACTION_CHOOSER); 469 intent.setPackage(KEYCHAIN_PACKAGE); 470 intent.putExtra(EXTRA_RESPONSE, new AliasResponse(response)); 471 intent.putExtra(EXTRA_URI, uri); 472 intent.putExtra(EXTRA_ALIAS, alias); 473 // the PendingIntent is used to get calling package name 474 intent.putExtra(EXTRA_SENDER, PendingIntent.getActivity(activity, 0, new Intent(), 0)); 475 activity.startActivity(intent); 476 } 477 478 private static class AliasResponse extends IKeyChainAliasCallback.Stub { 479 private final KeyChainAliasCallback keyChainAliasResponse; AliasResponse(KeyChainAliasCallback keyChainAliasResponse)480 private AliasResponse(KeyChainAliasCallback keyChainAliasResponse) { 481 this.keyChainAliasResponse = keyChainAliasResponse; 482 } alias(String alias)483 @Override public void alias(String alias) { 484 keyChainAliasResponse.alias(alias); 485 } 486 } 487 488 /** 489 * Returns the {@code PrivateKey} for the requested alias, or null 490 * if there is no result. 491 * 492 * <p> This method may block while waiting for a connection to another process, and must never 493 * be called from the main thread. 494 * <p> As {@link Activity} and {@link Service} contexts are short-lived and can be destroyed 495 * at any time from the main thread, it is safer to rely on a long-lived context such as one 496 * returned from {@link Context#getApplicationContext()}. 497 * 498 * @param alias The alias of the desired private key, typically returned via 499 * {@link KeyChainAliasCallback#alias}. 500 * @throws KeyChainException if the alias was valid but there was some problem accessing it. 501 * @throws IllegalStateException if called from the main thread. 502 */ 503 @Nullable @WorkerThread getPrivateKey(@onNull Context context, @NonNull String alias)504 public static PrivateKey getPrivateKey(@NonNull Context context, @NonNull String alias) 505 throws KeyChainException, InterruptedException { 506 KeyPair keyPair = getKeyPair(context, alias); 507 if (keyPair != null) { 508 return keyPair.getPrivate(); 509 } 510 511 return null; 512 } 513 514 /** @hide */ 515 @Nullable @WorkerThread getKeyPair(@onNull Context context, @NonNull String alias)516 public static KeyPair getKeyPair(@NonNull Context context, @NonNull String alias) 517 throws KeyChainException, InterruptedException { 518 if (alias == null) { 519 throw new NullPointerException("alias == null"); 520 } 521 if (context == null) { 522 throw new NullPointerException("context == null"); 523 } 524 525 final String keyId; 526 try (KeyChainConnection keyChainConnection = bind(context.getApplicationContext())) { 527 keyId = keyChainConnection.getService().requestPrivateKey(alias); 528 } catch (RemoteException e) { 529 throw new KeyChainException(e); 530 } catch (RuntimeException e) { 531 // only certain RuntimeExceptions can be propagated across the IKeyChainService call 532 throw new KeyChainException(e); 533 } 534 535 if (keyId == null) { 536 return null; 537 } else { 538 try { 539 return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore( 540 KeyStore.getInstance(), keyId, KeyStore.UID_SELF); 541 } catch (RuntimeException | UnrecoverableKeyException e) { 542 throw new KeyChainException(e); 543 } 544 } 545 } 546 547 /** 548 * Returns the {@code X509Certificate} chain for the requested 549 * alias, or null if there is no result. 550 * <p> 551 * <strong>Note:</strong> If a certificate chain was explicitly specified when the alias was 552 * installed, this method will return that chain. If only the client certificate was specified 553 * at the installation time, this method will try to build a certificate chain using all 554 * available trust anchors (preinstalled and user-added). 555 * 556 * <p> This method may block while waiting for a connection to another process, and must never 557 * be called from the main thread. 558 * <p> As {@link Activity} and {@link Service} contexts are short-lived and can be destroyed 559 * at any time from the main thread, it is safer to rely on a long-lived context such as one 560 * returned from {@link Context#getApplicationContext()}. 561 * 562 * @param alias The alias of the desired certificate chain, typically 563 * returned via {@link KeyChainAliasCallback#alias}. 564 * @throws KeyChainException if the alias was valid but there was some problem accessing it. 565 * @throws IllegalStateException if called from the main thread. 566 */ 567 @Nullable @WorkerThread getCertificateChain(@onNull Context context, @NonNull String alias)568 public static X509Certificate[] getCertificateChain(@NonNull Context context, 569 @NonNull String alias) throws KeyChainException, InterruptedException { 570 if (alias == null) { 571 throw new NullPointerException("alias == null"); 572 } 573 574 final byte[] certificateBytes; 575 final byte[] certChainBytes; 576 try (KeyChainConnection keyChainConnection = bind(context.getApplicationContext())) { 577 IKeyChainService keyChainService = keyChainConnection.getService(); 578 certificateBytes = keyChainService.getCertificate(alias); 579 if (certificateBytes == null) { 580 return null; 581 } 582 certChainBytes = keyChainService.getCaCertificates(alias); 583 } catch (RemoteException e) { 584 throw new KeyChainException(e); 585 } catch (RuntimeException e) { 586 // only certain RuntimeExceptions can be propagated across the IKeyChainService call 587 throw new KeyChainException(e); 588 } 589 590 try { 591 X509Certificate leafCert = toCertificate(certificateBytes); 592 // If the keypair is installed with a certificate chain by either 593 // DevicePolicyManager.installKeyPair or CertInstaller, return that chain. 594 if (certChainBytes != null && certChainBytes.length != 0) { 595 Collection<X509Certificate> chain = toCertificates(certChainBytes); 596 ArrayList<X509Certificate> fullChain = new ArrayList<>(chain.size() + 1); 597 fullChain.add(leafCert); 598 fullChain.addAll(chain); 599 return fullChain.toArray(new X509Certificate[fullChain.size()]); 600 } else { 601 // If there isn't a certificate chain, either due to a pre-existing keypair 602 // installed before N, or no chain is explicitly installed under the new logic, 603 // fall back to old behavior of constructing the chain from trusted credentials. 604 // 605 // This logic exists to maintain old behaviour for already installed keypair, at 606 // the cost of potentially returning extra certificate chain for new clients who 607 // explicitly installed only the client certificate without a chain. The latter 608 // case is actually no different from pre-N behaviour of getCertificateChain(), 609 // in that sense this change introduces no regression. Besides the returned chain 610 // is still valid so the consumer of the chain should have no problem verifying it. 611 TrustedCertificateStore store = new TrustedCertificateStore(); 612 List<X509Certificate> chain = store.getCertificateChain(leafCert); 613 return chain.toArray(new X509Certificate[chain.size()]); 614 } 615 } catch (CertificateException | RuntimeException e) { 616 throw new KeyChainException(e); 617 } 618 } 619 620 /** 621 * Returns {@code true} if the current device's {@code KeyChain} supports a 622 * specific {@code PrivateKey} type indicated by {@code algorithm} (e.g., 623 * "RSA"). 624 */ isKeyAlgorithmSupported( @onNull @eyProperties.KeyAlgorithmEnum String algorithm)625 public static boolean isKeyAlgorithmSupported( 626 @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) { 627 final String algUpper = algorithm.toUpperCase(Locale.US); 628 return KeyProperties.KEY_ALGORITHM_EC.equals(algUpper) 629 || KeyProperties.KEY_ALGORITHM_RSA.equals(algUpper); 630 } 631 632 /** 633 * Returns {@code true} if the current device's {@code KeyChain} binds any 634 * {@code PrivateKey} of the given {@code algorithm} to the device once 635 * imported or generated. This can be used to tell if there is special 636 * hardware support that can be used to bind keys to the device in a way 637 * that makes it non-exportable. 638 * 639 * @deprecated Whether the key is bound to the secure hardware is known only 640 * once the key has been imported. To find out, use: 641 * <pre>{@code 642 * PrivateKey key = ...; // private key from KeyChain 643 * 644 * KeyFactory keyFactory = 645 * KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore"); 646 * KeyInfo keyInfo = keyFactory.getKeySpec(key, KeyInfo.class); 647 * if (keyInfo.isInsideSecureHardware()) { 648 * // The key is bound to the secure hardware of this Android 649 * }}</pre> 650 */ 651 @Deprecated isBoundKeyAlgorithm( @onNull @eyProperties.KeyAlgorithmEnum String algorithm)652 public static boolean isBoundKeyAlgorithm( 653 @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) { 654 if (!isKeyAlgorithmSupported(algorithm)) { 655 return false; 656 } 657 658 return KeyStore.getInstance().isHardwareBacked(algorithm); 659 } 660 661 /** @hide */ 662 @NonNull toCertificate(@onNull byte[] bytes)663 public static X509Certificate toCertificate(@NonNull byte[] bytes) { 664 if (bytes == null) { 665 throw new IllegalArgumentException("bytes == null"); 666 } 667 try { 668 CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 669 Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes)); 670 return (X509Certificate) cert; 671 } catch (CertificateException e) { 672 throw new AssertionError(e); 673 } 674 } 675 676 /** @hide */ 677 @NonNull toCertificates(@onNull byte[] bytes)678 public static Collection<X509Certificate> toCertificates(@NonNull byte[] bytes) { 679 if (bytes == null) { 680 throw new IllegalArgumentException("bytes == null"); 681 } 682 try { 683 CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 684 return (Collection<X509Certificate>) certFactory.generateCertificates( 685 new ByteArrayInputStream(bytes)); 686 } catch (CertificateException e) { 687 throw new AssertionError(e); 688 } 689 } 690 691 /** 692 * @hide for reuse by CertInstaller and Settings. 693 * @see KeyChain#bind 694 */ 695 public static class KeyChainConnection implements Closeable { 696 private final Context context; 697 private final ServiceConnection serviceConnection; 698 private final IKeyChainService service; KeyChainConnection(Context context, ServiceConnection serviceConnection, IKeyChainService service)699 protected KeyChainConnection(Context context, 700 ServiceConnection serviceConnection, 701 IKeyChainService service) { 702 this.context = context; 703 this.serviceConnection = serviceConnection; 704 this.service = service; 705 } close()706 @Override public void close() { 707 context.unbindService(serviceConnection); 708 } getService()709 public IKeyChainService getService() { 710 return service; 711 } 712 } 713 714 /** 715 * @hide for reuse by CertInstaller and Settings. 716 * 717 * Caller should call unbindService on the result when finished. 718 */ 719 @WorkerThread bind(@onNull Context context)720 public static KeyChainConnection bind(@NonNull Context context) throws InterruptedException { 721 return bindAsUser(context, Process.myUserHandle()); 722 } 723 724 /** 725 * @hide 726 */ 727 @WorkerThread bindAsUser(@onNull Context context, UserHandle user)728 public static KeyChainConnection bindAsUser(@NonNull Context context, UserHandle user) 729 throws InterruptedException { 730 if (context == null) { 731 throw new NullPointerException("context == null"); 732 } 733 ensureNotOnMainThread(context); 734 final BlockingQueue<IKeyChainService> q = new LinkedBlockingQueue<IKeyChainService>(1); 735 ServiceConnection keyChainServiceConnection = new ServiceConnection() { 736 volatile boolean mConnectedAtLeastOnce = false; 737 @Override public void onServiceConnected(ComponentName name, IBinder service) { 738 if (!mConnectedAtLeastOnce) { 739 mConnectedAtLeastOnce = true; 740 try { 741 q.put(IKeyChainService.Stub.asInterface(Binder.allowBlocking(service))); 742 } catch (InterruptedException e) { 743 // will never happen, since the queue starts with one available slot 744 } 745 } 746 } 747 @Override public void onServiceDisconnected(ComponentName name) {} 748 }; 749 Intent intent = new Intent(IKeyChainService.class.getName()); 750 ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); 751 intent.setComponent(comp); 752 if (comp == null || !context.bindServiceAsUser( 753 intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, user)) { 754 throw new AssertionError("could not bind to KeyChainService"); 755 } 756 return new KeyChainConnection(context, keyChainServiceConnection, q.take()); 757 } 758 ensureNotOnMainThread(@onNull Context context)759 private static void ensureNotOnMainThread(@NonNull Context context) { 760 Looper looper = Looper.myLooper(); 761 if (looper != null && looper == context.getMainLooper()) { 762 throw new IllegalStateException( 763 "calling this from your main thread can lead to deadlock"); 764 } 765 } 766 } 767