• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static android.security.Credentials.ACTION_MANAGE_CREDENTIALS;
19 
20 import android.Manifest;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.RequiresPermission;
24 import android.annotation.SdkConstant;
25 import android.annotation.SdkConstant.SdkConstantType;
26 import android.annotation.SystemApi;
27 import android.annotation.TestApi;
28 import android.annotation.WorkerThread;
29 import android.app.Activity;
30 import android.app.PendingIntent;
31 import android.app.Service;
32 import android.content.ComponentName;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.content.ServiceConnection;
36 import android.net.Uri;
37 import android.os.Binder;
38 import android.os.Handler;
39 import android.os.IBinder;
40 import android.os.Looper;
41 import android.os.Process;
42 import android.os.RemoteException;
43 import android.os.UserHandle;
44 import android.os.UserManager;
45 import android.security.keystore.KeyPermanentlyInvalidatedException;
46 import android.security.keystore.KeyProperties;
47 import android.system.keystore2.Domain;
48 import android.system.keystore2.KeyDescriptor;
49 import android.util.Log;
50 
51 import com.android.org.conscrypt.TrustedCertificateStore;
52 
53 import java.io.ByteArrayInputStream;
54 import java.io.Closeable;
55 import java.io.Serializable;
56 import java.security.KeyPair;
57 import java.security.Principal;
58 import java.security.PrivateKey;
59 import java.security.UnrecoverableKeyException;
60 import java.security.cert.Certificate;
61 import java.security.cert.CertificateException;
62 import java.security.cert.CertificateFactory;
63 import java.security.cert.X509Certificate;
64 import java.util.ArrayList;
65 import java.util.Collection;
66 import java.util.List;
67 import java.util.Locale;
68 import java.util.concurrent.CountDownLatch;
69 import java.util.concurrent.TimeUnit;
70 import java.util.concurrent.atomic.AtomicReference;
71 
72 import javax.security.auth.x500.X500Principal;
73 
74 /**
75  * The {@code KeyChain} class provides access to private keys and
76  * their corresponding certificate chains in credential storage.
77  *
78  * <p>Applications accessing the {@code KeyChain} normally go through
79  * these steps:
80  *
81  * <ol>
82  *
83  * <li>Receive a callback from an {@link javax.net.ssl.X509KeyManager
84  * X509KeyManager} that a private key is requested.
85  *
86  * <li>Call {@link #choosePrivateKeyAlias
87  * choosePrivateKeyAlias} to allow the user to select from a
88  * list of currently available private keys and corresponding
89  * certificate chains. The chosen alias will be returned by the
90  * callback {@link KeyChainAliasCallback#alias}, or null if no private
91  * key is available or the user cancels the request.
92  *
93  * <li>Call {@link #getPrivateKey} and {@link #getCertificateChain} to
94  * retrieve the credentials to return to the corresponding {@link
95  * javax.net.ssl.X509KeyManager} callbacks.
96  *
97  * </ol>
98  *
99  * <p>An application may remember the value of a selected alias to
100  * avoid prompting the user with {@link #choosePrivateKeyAlias
101  * choosePrivateKeyAlias} on subsequent connections. If the alias is
102  * no longer valid, null will be returned on lookups using that value
103  *
104  * <p>An application can request the installation of private keys and
105  * certificates via the {@code Intent} provided by {@link
106  * #createInstallIntent}. Private keys installed via this {@code
107  * Intent} will be accessible via {@link #choosePrivateKeyAlias} while
108  * Certificate Authority (CA) certificates will be trusted by all
109  * applications through the default {@code X509TrustManager}.
110  */
111 // TODO reference intent for credential installation when public
112 public final class KeyChain {
113 
114     /**
115      * @hide
116      */
117     public static final String LOG = "KeyChain";
118 
119     /**
120      * @hide Also used by KeyChainService implementation
121      */
122     public static final String ACCOUNT_TYPE = "com.android.keychain";
123 
124     /**
125      * Package name for KeyChain chooser.
126      */
127     private static final String KEYCHAIN_PACKAGE = "com.android.keychain";
128 
129     /**
130      * Action to bring up the KeyChainActivity
131      */
132     private static final String ACTION_CHOOSER = "com.android.keychain.CHOOSER";
133 
134     /**
135      * Package name for the Certificate Installer.
136      */
137     private static final String CERT_INSTALLER_PACKAGE = "com.android.certinstaller";
138 
139     /**
140      * Package name for Settings.
141      */
142     private static final String SETTINGS_PACKAGE = "com.android.settings";
143 
144     /**
145      * Extra for use with {@link #ACTION_CHOOSER}
146      * @hide Also used by KeyChainActivity implementation
147      */
148     public static final String EXTRA_RESPONSE = "response";
149 
150     /**
151      * Extra for use with {@link #ACTION_CHOOSER}
152      * @hide Also used by KeyChainActivity implementation
153      */
154     public static final String EXTRA_URI = "uri";
155 
156     /**
157      * Extra for use with {@link #ACTION_CHOOSER}
158      * @hide Also used by KeyChainActivity implementation
159      */
160     public static final String EXTRA_ALIAS = "alias";
161 
162     /**
163      * Extra for use with {@link #ACTION_CHOOSER}
164      * @hide Also used by KeyChainActivity implementation
165      */
166     public static final String EXTRA_SENDER = "sender";
167 
168     /**
169      * Extra for use with {@link #ACTION_CHOOSER}
170      * @hide Also used by KeyChainActivity implementation
171      */
172     public static final String EXTRA_KEY_TYPES = "key_types";
173 
174     /**
175      * Extra for use with {@link #ACTION_CHOOSER}
176      * @hide Also used by KeyChainActivity implementation
177      */
178     public static final String EXTRA_ISSUERS = "issuers";
179 
180     /**
181      * Action to bring up the CertInstaller.
182      */
183     private static final String ACTION_INSTALL = "android.credentials.INSTALL";
184 
185     /**
186      * Optional extra to specify a {@code String} credential name on
187      * the {@code Intent} returned by {@link #createInstallIntent}.
188      */
189     // Compatible with old com.android.certinstaller.CredentialHelper.CERT_NAME_KEY
190     public static final String EXTRA_NAME = "name";
191 
192     /**
193      * Optional extra to specify an X.509 certificate to install on
194      * the {@code Intent} returned by {@link #createInstallIntent}.
195      * The extra value should be a PEM or ASN.1 DER encoded {@code
196      * byte[]}. An {@link X509Certificate} can be converted to DER
197      * encoded bytes with {@link X509Certificate#getEncoded}.
198      *
199      * <p>{@link #EXTRA_NAME} may be used to provide a default alias
200      * name for the installed certificate.
201      */
202     // Compatible with old android.security.Credentials.CERTIFICATE
203     public static final String EXTRA_CERTIFICATE = "CERT";
204 
205     /**
206      * Optional extra for use with the {@code Intent} returned by
207      * {@link #createInstallIntent} to specify a PKCS#12 key store to
208      * install. The extra value should be a {@code byte[]}. The bytes
209      * may come from an external source or be generated with {@link
210      * java.security.KeyStore#store} on a "PKCS12" instance.
211      *
212      * <p>The user will be prompted for the password to load the key store.
213      *
214      * <p>The key store will be scanned for {@link
215      * java.security.KeyStore.PrivateKeyEntry} entries and both the
216      * private key and associated certificate chain will be installed.
217      *
218      * <p>{@link #EXTRA_NAME} may be used to provide a default alias
219      * name for the installed credentials.
220      */
221     // Compatible with old android.security.Credentials.PKCS12
222     public static final String EXTRA_PKCS12 = "PKCS12";
223 
224     /**
225      * Extra used by {@link #createManageCredentialsIntent(AppUriAuthenticationPolicy)} to specify
226      * the authentication policy of the credential management app.
227      *
228      * <p>The authentication policy declares which alias for a private key and certificate pair
229      * should be used for authentication, given a list of apps and URIs.
230      *
231      * <p>The extra value should be a {@link AppUriAuthenticationPolicy}.
232      *
233      * @hide
234      */
235     public static final String EXTRA_AUTHENTICATION_POLICY =
236             "android.security.extra.AUTHENTICATION_POLICY";
237 
238     /**
239      * Broadcast Action: Indicates the trusted storage has changed. Sent when
240      * one of this happens:
241      *
242      * <ul>
243      * <li>a new CA is added,
244      * <li>an existing CA is removed or disabled,
245      * <li>a disabled CA is enabled,
246      * <li>trusted storage is reset (all user certs are cleared),
247      * <li>when permission to access a private key is changed.
248      * </ul>
249      *
250      * @deprecated Use {@link #ACTION_KEYCHAIN_CHANGED}, {@link #ACTION_TRUST_STORE_CHANGED} or
251      * {@link #ACTION_KEY_ACCESS_CHANGED}. Apps that target a version higher than
252      * {@link android.os.Build.VERSION_CODES#N_MR1} will only receive this broadcast if they
253      * register for it at runtime.
254      */
255     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
256     public static final String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED";
257 
258     /**
259      * Broadcast Action: Indicates the contents of the keychain has changed. Sent when a KeyChain
260      * entry is added, modified or removed.
261      */
262     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
263     public static final String ACTION_KEYCHAIN_CHANGED = "android.security.action.KEYCHAIN_CHANGED";
264 
265     /**
266      * Broadcast Action: Indicates the contents of the trusted certificate store has changed. Sent
267      * when one the following occurs:
268      *
269      * <ul>
270      * <li>A pre-installed CA is disabled or re-enabled</li>
271      * <li>A CA is added or removed from the trust store</li>
272      * </ul>
273      */
274     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
275     public static final String ACTION_TRUST_STORE_CHANGED =
276             "android.security.action.TRUST_STORE_CHANGED";
277 
278     /**
279      * Broadcast Action: Indicates that the access permissions for a private key have changed.
280      *
281      */
282     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
283     public static final String ACTION_KEY_ACCESS_CHANGED =
284             "android.security.action.KEY_ACCESS_CHANGED";
285 
286     /**
287      * Used as a String extra field in {@link #ACTION_KEY_ACCESS_CHANGED} to supply the alias of
288      * the key.
289      */
290     public static final String EXTRA_KEY_ALIAS = "android.security.extra.KEY_ALIAS";
291 
292     /**
293      * Used as a boolean extra field in {@link #ACTION_KEY_ACCESS_CHANGED} to supply if the key is
294      * accessible to the application.
295      */
296     public static final String EXTRA_KEY_ACCESSIBLE = "android.security.extra.KEY_ACCESSIBLE";
297 
298     /**
299      * Indicates that a call to {@link #generateKeyPair} was successful.
300      * @hide
301      */
302     public static final int KEY_GEN_SUCCESS = 0;
303 
304     /**
305      * An alias was missing from the key specifications when calling {@link #generateKeyPair}.
306      * @hide
307      */
308     public static final int KEY_GEN_MISSING_ALIAS = 1;
309 
310     /**
311      * A key attestation challenge was provided to {@link #generateKeyPair}, but it shouldn't
312      * have been provided.
313      * @hide
314      */
315     public static final int KEY_GEN_SUPERFLUOUS_ATTESTATION_CHALLENGE = 2;
316 
317     /**
318      * Algorithm not supported by {@link #generateKeyPair}
319      * @hide
320      */
321     public static final int KEY_GEN_NO_SUCH_ALGORITHM = 3;
322 
323     /**
324      * Invalid algorithm parameters when calling {@link #generateKeyPair}
325      * @hide
326      */
327     public static final int KEY_GEN_INVALID_ALGORITHM_PARAMETERS = 4;
328 
329     /**
330      * Keystore is not available when calling {@link #generateKeyPair}
331      * @hide
332      */
333     public static final int KEY_GEN_NO_KEYSTORE_PROVIDER = 5;
334 
335     /**
336      * StrongBox unavailable when calling {@link #generateKeyPair}
337      * @hide
338      */
339     public static final int KEY_GEN_STRONGBOX_UNAVAILABLE = 6;
340 
341     /**
342      * General failure while calling {@link #generateKeyPair}
343      * @hide
344      */
345     public static final int KEY_GEN_FAILURE = 7;
346 
347     /**
348      * Successful call to {@link #attestKey}
349      * @hide
350      */
351     public static final int KEY_ATTESTATION_SUCCESS = 0;
352 
353     /**
354      * Attestation challenge missing when calling {@link #attestKey}
355      * @hide
356      */
357     public static final int KEY_ATTESTATION_MISSING_CHALLENGE = 1;
358 
359     /**
360      * The caller requested Device ID attestation when calling {@link #attestKey}, but has no
361      * permissions to get device identifiers.
362      * @hide
363      */
364     public static final int KEY_ATTESTATION_CANNOT_COLLECT_DATA = 2;
365 
366     /**
367      * The underlying hardware does not support Device ID attestation or cannot attest to the
368      * identifiers that are stored on the device. This indicates permanent inability
369      * to get attestation records on the device.
370      * @hide
371      */
372     public static final int KEY_ATTESTATION_CANNOT_ATTEST_IDS = 3;
373 
374     /**
375      * General failure when calling {@link #attestKey}
376      * @hide
377      */
378     public static final int KEY_ATTESTATION_FAILURE = 4;
379 
380     private static final int BIND_KEY_CHAIN_SERVICE_TIMEOUT_MS = 30 * 1000;
381 
382     /**
383      * Used by DPC or delegated app in
384      * {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias} or
385      * {@link android.app.admin.DelegatedAdminReceiver#onChoosePrivateKeyAlias} to identify that
386      * the requesting app is not granted access to any key, and nor will the user be able to grant
387      * access manually.
388      */
389     public static final String KEY_ALIAS_SELECTION_DENIED =
390             "android:alias-selection-denied";
391 
392     /**
393      * Returns an {@code Intent} that can be used for credential
394      * installation. The intent may be used without any extras, in
395      * which case the user will be able to install credentials from
396      * their own source.
397      *
398      * <p>Alternatively, {@link #EXTRA_CERTIFICATE} or {@link
399      * #EXTRA_PKCS12} maybe used to specify the bytes of an X.509
400      * certificate or a PKCS#12 key store for installation. These
401      * extras may be combined with {@link #EXTRA_NAME} to provide a
402      * default alias name for credentials being installed.
403      *
404      * <p>When used with {@link Activity#startActivityForResult},
405      * {@link Activity#RESULT_OK} will be returned if a credential was
406      * successfully installed, otherwise {@link
407      * Activity#RESULT_CANCELED} will be returned.
408      *
409      * <p>Starting from {@link android.os.Build.VERSION_CODES#R}, the intent returned by this
410      * method cannot be used for installing CA certificates. Since CA certificates can only be
411      * installed via Settings, the app should provide the user with a file containing the
412      * CA certificate. One way to do this would be to use the {@link android.provider.MediaStore}
413      * API to write the certificate to the {@link android.provider.MediaStore.Downloads}
414      * collection.
415      */
416     @NonNull
createInstallIntent()417     public static Intent createInstallIntent() {
418         Intent intent = new Intent(ACTION_INSTALL);
419         intent.setClassName(CERT_INSTALLER_PACKAGE,
420                             "com.android.certinstaller.CertInstallerMain");
421         return intent;
422     }
423 
424     /**
425      * Returns an {@code Intent} that should be used by an app to request to manage the user's
426      * credentials. This is limited to unmanaged devices. The authentication policy must be
427      * provided to be able to make this request successfully.
428      *
429      * <p> This intent should be started using {@link Activity#startActivityForResult(Intent, int)}
430      * to verify whether the request was successful and whether the user accepted or denied the
431      * request. If the user successfully receives and accepts the request, the result code will be
432      * {@link Activity#RESULT_OK}, otherwise the result code will be
433      * {@link Activity#RESULT_CANCELED}.
434      *
435      * <p> {@link KeyChain#isCredentialManagementApp(Context)} should be used to determine whether
436      * an app is already the credential management app.
437      *
438      * @param policy The authentication policy determines which alias for a private key and
439      *               certificate pair should be used for authentication.
440      */
441     @NonNull
createManageCredentialsIntent(@onNull AppUriAuthenticationPolicy policy)442     public static Intent createManageCredentialsIntent(@NonNull AppUriAuthenticationPolicy policy) {
443         Intent intent = new Intent(ACTION_MANAGE_CREDENTIALS);
444         intent.setComponent(ComponentName.createRelative(SETTINGS_PACKAGE,
445                 ".security.RequestManageCredentials"));
446         intent.putExtra(EXTRA_AUTHENTICATION_POLICY, policy);
447         return intent;
448     }
449 
450     /**
451      * Launches an {@code Activity} for the user to select the alias
452      * for a private key and certificate pair for authentication. The
453      * selected alias or null will be returned via the
454      * KeyChainAliasCallback callback.
455      *
456      * <p>A device policy controller (as a device or profile owner) can
457      * intercept the request before the activity is shown, to pick a
458      * specific private key alias by implementing
459      * {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias
460      * onChoosePrivateKeyAlias}.
461      *
462      * <p>{@code keyTypes} and {@code issuers} may be used to
463      * narrow down suggested choices to the user. If either {@code keyTypes}
464      * or {@code issuers} is specified and non-empty, and there are no
465      * matching certificates in the KeyChain, then the certificate
466      * selection prompt would be suppressed entirely.
467      *
468      * <p>{@code host} and {@code port} may be used to give the user
469      * more context about the server requesting the credentials.
470      *
471      * <p>{@code alias} allows the caller to preselect an existing
472      * alias which will still be subject to user confirmation.
473      *
474      * @param activity The {@link Activity} context to use for
475      *     launching the new sub-Activity to prompt the user to select
476      *     a private key; used only to call startActivity(); must not
477      *     be null.
478      * @param response Callback to invoke when the request completes;
479      *     must not be null.
480      * @param keyTypes The acceptable types of asymmetric keys such as
481      *     "RSA", "EC" or null.
482      * @param issuers The acceptable certificate issuers for the
483      *     certificate matching the private key, or null.
484      * @param host The host name of the server requesting the
485      *     certificate, or null if unavailable.
486      * @param port The port number of the server requesting the
487      *     certificate, or -1 if unavailable.
488      * @param alias The alias to preselect if available, or null if
489      *     unavailable.
490      */
choosePrivateKeyAlias(@onNull Activity activity, @NonNull KeyChainAliasCallback response, @Nullable @KeyProperties.KeyAlgorithmEnum String[] keyTypes, @Nullable Principal[] issuers, @Nullable String host, int port, @Nullable String alias)491     public static void choosePrivateKeyAlias(@NonNull Activity activity,
492             @NonNull KeyChainAliasCallback response,
493             @Nullable @KeyProperties.KeyAlgorithmEnum String[] keyTypes,
494             @Nullable Principal[] issuers,
495             @Nullable String host, int port, @Nullable String alias) {
496         Uri uri = null;
497         if (host != null) {
498             uri = new Uri.Builder()
499                     .authority(host + (port != -1 ? ":" + port : ""))
500                     .build();
501         }
502         choosePrivateKeyAlias(activity, response, keyTypes, issuers, uri, alias);
503     }
504 
505     /**
506      * Launches an {@code Activity} for the user to select the alias
507      * for a private key and certificate pair for authentication. The
508      * selected alias or null will be returned via the
509      * KeyChainAliasCallback callback.
510      *
511      * <p>A device policy controller (as a device or profile owner) can
512      * intercept the request before the activity is shown, to pick a
513      * specific private key alias by implementing
514      * {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias
515      * onChoosePrivateKeyAlias}.
516      *
517      * <p>{@code keyTypes} and {@code issuers} may be used to
518      * narrow down suggested choices to the user. If either {@code keyTypes}
519      * or {@code issuers} is specified and non-empty, and there are no
520      * matching certificates in the KeyChain, then the certificate
521      * selection prompt would be suppressed entirely.
522      *
523      * <p>{@code uri} may be used to give the user more context about
524      * the server requesting the credentials.
525      *
526      * <p>{@code alias} allows the caller to preselect an existing
527      * alias which will still be subject to user confirmation.
528      *
529      * @param activity The {@link Activity} context to use for
530      *     launching the new sub-Activity to prompt the user to select
531      *     a private key; used only to call startActivity(); must not
532      *     be null.
533      * @param response Callback to invoke when the request completes;
534      *     must not be null.
535      * @param keyTypes The acceptable types of asymmetric keys such as
536      *     "RSA", "EC" or null.
537      * @param issuers The acceptable certificate issuers for the
538      *     certificate matching the private key, or null.
539      * @param uri The full URI the server is requesting the certificate
540      *     for, or null if unavailable.
541      * @param alias The alias to preselect if available, or null if
542      *     unavailable.
543      * @throws IllegalArgumentException if the specified issuers are not
544      *     of type {@code X500Principal}.
545      */
choosePrivateKeyAlias(@onNull Activity activity, @NonNull KeyChainAliasCallback response, @Nullable @KeyProperties.KeyAlgorithmEnum String[] keyTypes, @Nullable Principal[] issuers, @Nullable Uri uri, @Nullable String alias)546     public static void choosePrivateKeyAlias(@NonNull Activity activity,
547             @NonNull KeyChainAliasCallback response,
548             @Nullable @KeyProperties.KeyAlgorithmEnum String[] keyTypes,
549             @Nullable Principal[] issuers,
550             @Nullable Uri uri, @Nullable String alias) {
551         /*
552          * Specifying keyTypes excludes certificates with different key types
553          * from the list of certificates presented to the user.
554          * In practice today, most servers would require RSA or EC
555          * certificates.
556          *
557          * Specifying issuers narrows down the list by filtering out
558          * certificates with issuers which are not matching the provided ones.
559          * This has been reported to Chrome several times (crbug.com/731769).
560          * There's no concrete description on what to do when the client has no
561          * certificates that match the provided issuers.
562          * To be conservative, Android will not present the user with any
563          * certificates to choose from.
564          * If the list of issuers is empty then the client may send any
565          * certificate, see:
566          * https://tools.ietf.org/html/rfc5246#section-7.4.4
567          */
568         if (activity == null) {
569             throw new NullPointerException("activity == null");
570         }
571         if (response == null) {
572             throw new NullPointerException("response == null");
573         }
574         Intent intent = new Intent(ACTION_CHOOSER);
575         intent.setPackage(KEYCHAIN_PACKAGE);
576         intent.putExtra(EXTRA_RESPONSE, new AliasResponse(response));
577         intent.putExtra(EXTRA_URI, uri);
578         intent.putExtra(EXTRA_ALIAS, alias);
579         intent.putExtra(EXTRA_KEY_TYPES, keyTypes);
580         ArrayList<byte[]> issuersList = new ArrayList();
581         if (issuers != null) {
582             for (Principal issuer: issuers) {
583                 // In a TLS client context (like Chrome), issuers would only
584                 // be specified as X500Principals. No other use cases for
585                 // specifying principals have been brought up. Under these
586                 // circumstances, only allow issuers specified as
587                 // X500Principals.
588                 if (!(issuer instanceof X500Principal)) {
589                     throw new IllegalArgumentException(String.format(
590                             "Issuer %s is of type %s, not X500Principal",
591                             issuer.toString(), issuer.getClass()));
592                 }
593                 // Pass the DER-encoded issuer as that's the most accurate
594                 // representation and what is sent over the wire.
595                 issuersList.add(((X500Principal) issuer).getEncoded());
596             }
597         }
598         intent.putExtra(EXTRA_ISSUERS, (Serializable) issuersList);
599         // the PendingIntent is used to get calling package name
600         intent.putExtra(EXTRA_SENDER, PendingIntent.getActivity(activity, 0, new Intent(),
601                 PendingIntent.FLAG_IMMUTABLE));
602         activity.startActivity(intent);
603     }
604 
605     /**
606      * Check whether the caller is the credential management app {@code CredentialManagementApp}.
607      * The credential management app has the ability to manage the user's KeyChain credentials
608      * on unmanaged devices.
609      *
610      * <p> {@link KeyChain#createManageCredentialsIntent} should be used by an app to request to
611      * become the credential management app. The user must approve this request before the app can
612      * manage the user's credentials. There can only be one credential management on the device.
613      *
614      * @return {@code true} if the caller is the credential management app.
615      */
616     @WorkerThread
isCredentialManagementApp(@onNull Context context)617     public static boolean isCredentialManagementApp(@NonNull Context context) {
618         boolean isCredentialManagementApp = false;
619         try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
620             isCredentialManagementApp = keyChainConnection.getService()
621                     .isCredentialManagementApp(context.getPackageName());
622         } catch (RemoteException e) {
623             e.rethrowAsRuntimeException();
624         } catch (InterruptedException e) {
625             throw new RuntimeException("Interrupted while checking whether the caller is the "
626                     + "credential management app.", e);
627         } catch (SecurityException e) {
628             isCredentialManagementApp = false;
629         }
630         return isCredentialManagementApp;
631     }
632 
633     /**
634      * Called by the credential management app to get the authentication policy
635      * {@link AppUriAuthenticationPolicy}.
636      *
637      * @return the credential management app's authentication policy.
638      * @throws SecurityException if the caller is not the credential management app.
639      */
640     @WorkerThread
641     @NonNull
getCredentialManagementAppPolicy( @onNull Context context)642     public static AppUriAuthenticationPolicy getCredentialManagementAppPolicy(
643             @NonNull Context context) throws SecurityException {
644         AppUriAuthenticationPolicy policy = null;
645         try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
646             policy = keyChainConnection.getService().getCredentialManagementAppPolicy();
647         } catch (RemoteException e) {
648             e.rethrowAsRuntimeException();
649         } catch (InterruptedException e) {
650             throw new RuntimeException(
651                     "Interrupted while getting credential management app policy.", e);
652         }
653         return policy;
654     }
655 
656     /**
657      * Set a credential management app. The credential management app has the ability to manage
658      * the user's KeyChain credentials on unmanaged devices.
659      *
660      * <p>There can only be one credential management on the device. If another app requests to
661      * become the credential management app, then the existing credential management app will
662      * no longer be able to manage credentials.
663      *
664      * @param packageName The package name of the credential management app
665      * @param authenticationPolicy The authentication policy of the credential management app. This
666      *                             policy determines which alias for a private key and certificate
667      *                             pair should be used for authentication.
668      * @return {@code true} if the credential management app was successfully added.
669      * @hide
670      */
671     @TestApi
672     @WorkerThread
673     @RequiresPermission(Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP)
setCredentialManagementApp(@onNull Context context, @NonNull String packageName, @NonNull AppUriAuthenticationPolicy authenticationPolicy)674     public static boolean setCredentialManagementApp(@NonNull Context context,
675             @NonNull String packageName, @NonNull AppUriAuthenticationPolicy authenticationPolicy) {
676         try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
677             keyChainConnection.getService()
678                     .setCredentialManagementApp(packageName, authenticationPolicy);
679             return true;
680         } catch (RemoteException | InterruptedException e) {
681             Log.w(LOG, "Set credential management app failed", e);
682             Thread.currentThread().interrupt();
683             return false;
684         }
685     }
686 
687     /**
688      * Called by the credential management app {@code CredentialManagementApp} to unregister as
689      * the credential management app and stop managing the user's credentials.
690      *
691      * <p> All credentials previously installed by the credential management app will be removed
692      * from the user's device.
693      *
694      * <p> An app holding {@code MANAGE_CREDENTIAL_MANAGEMENT_APP} permission can also call this
695      * method to remove the current credential management app, even if it's not the current
696      * credential management app itself.
697      *
698      * @return {@code true} if the credential management app was successfully removed.
699      */
700     @WorkerThread
701     @RequiresPermission(value = Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP,
702             conditional = true)
removeCredentialManagementApp(@onNull Context context)703     public static boolean removeCredentialManagementApp(@NonNull Context context) {
704         try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
705             keyChainConnection.getService().removeCredentialManagementApp();
706             return true;
707         } catch (RemoteException | InterruptedException e) {
708             Log.w(LOG, "Remove credential management app failed", e);
709             Thread.currentThread().interrupt();
710             return false;
711         }
712     }
713 
714     private static class AliasResponse extends IKeyChainAliasCallback.Stub {
715         private final KeyChainAliasCallback keyChainAliasResponse;
AliasResponse(KeyChainAliasCallback keyChainAliasResponse)716         private AliasResponse(KeyChainAliasCallback keyChainAliasResponse) {
717             this.keyChainAliasResponse = keyChainAliasResponse;
718         }
alias(String alias)719         @Override public void alias(String alias) {
720             keyChainAliasResponse.alias(alias);
721         }
722     }
723 
724     /**
725      * Returns the {@code PrivateKey} for the requested alias, or null if the alias does not exist
726      * or the caller has no permission to access it (see note on exceptions below).
727      *
728      * <p> This method may block while waiting for a connection to another process, and must never
729      * be called from the main thread.
730      * <p> As {@link Activity} and {@link Service} contexts are short-lived and can be destroyed
731      * at any time from the main thread, it is safer to rely on a long-lived context such as one
732      * returned from {@link Context#getApplicationContext()}.
733      *
734      * <p> If the caller provides a valid alias to which it was not granted access, then the
735      * caller must invoke {@link #choosePrivateKeyAlias} again to get another valid alias
736      * or a grant to access the same alias.
737      * <p>On Android versions prior to Q, when a key associated with the specified alias is
738      * unavailable, the method will throw a {@code KeyChainException} rather than return null.
739      * If the exception's cause (as obtained by calling {@code KeyChainException.getCause()})
740      * is a throwable of type {@code IllegalStateException} then the caller lacks a grant
741      * to access the key and certificates associated with this alias.
742      *
743      * @param alias The alias of the desired private key, typically returned via
744      *              {@link KeyChainAliasCallback#alias}.
745      * @throws KeyChainException if the alias was valid but there was some problem accessing it.
746      * @throws IllegalStateException if called from the main thread.
747      */
748     @Nullable @WorkerThread
getPrivateKey(@onNull Context context, @NonNull String alias)749     public static PrivateKey getPrivateKey(@NonNull Context context, @NonNull String alias)
750             throws KeyChainException, InterruptedException {
751         KeyPair keyPair = getKeyPair(context, alias);
752         if (keyPair != null) {
753             return keyPair.getPrivate();
754         }
755 
756         return null;
757     }
758 
759     /**
760      * This prefix is used to disambiguate grant aliase strings from normal key alias strings.
761      * Technically, a key alias string can use the same prefix. However, a collision does not
762      * lead to privilege escalation, because grants are access controlled in the Keystore daemon.
763      * @hide
764      */
765     public static final String GRANT_ALIAS_PREFIX = "ks2_keychain_grant_id:";
766 
getGrantDescriptor(String keyid)767     private static KeyDescriptor getGrantDescriptor(String keyid) {
768         KeyDescriptor result = new KeyDescriptor();
769         result.domain = Domain.GRANT;
770         result.blob = null;
771         result.alias = null;
772         try {
773             result.nspace = Long.parseUnsignedLong(
774                     keyid.substring(GRANT_ALIAS_PREFIX.length()), 16 /* radix */);
775         } catch (NumberFormatException e) {
776             return null;
777         }
778         return result;
779     }
780 
781     /** @hide */
getGrantString(KeyDescriptor key)782     public static String getGrantString(KeyDescriptor key) {
783         return String.format(GRANT_ALIAS_PREFIX + "%016X", key.nspace);
784     }
785 
786     /** @hide */
787     @Nullable @WorkerThread
getKeyPair(@onNull Context context, @NonNull String alias)788     public static KeyPair getKeyPair(@NonNull Context context, @NonNull String alias)
789             throws KeyChainException, InterruptedException {
790         if (alias == null) {
791             throw new NullPointerException("alias == null");
792         }
793         if (context == null) {
794             throw new NullPointerException("context == null");
795         }
796 
797         final String keyId;
798         try (KeyChainConnection keyChainConnection = bind(context.getApplicationContext())) {
799             keyId = keyChainConnection.getService().requestPrivateKey(alias);
800         } catch (RemoteException e) {
801             throw new KeyChainException(e);
802         } catch (RuntimeException e) {
803             // only certain RuntimeExceptions can be propagated across the IKeyChainService call
804             throw new KeyChainException(e);
805         }
806 
807         if (keyId == null) {
808             return null;
809         }
810 
811         try {
812             return android.security.keystore2.AndroidKeyStoreProvider
813                     .loadAndroidKeyStoreKeyPairFromKeystore(
814                             KeyStore2.getInstance(),
815                             getGrantDescriptor(keyId));
816         } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) {
817             throw new KeyChainException(e);
818         }
819     }
820 
821     /**
822      * Returns the {@code X509Certificate} chain for the requested alias, or null if the alias
823      * does not exist or the caller has no permission to access it (see note on exceptions
824      * in {@link #getPrivateKey}).
825      *
826      * <p>
827      * <strong>Note:</strong> If a certificate chain was explicitly specified when the alias was
828      * installed, this method will return that chain. If only the client certificate was specified
829      * at the installation time, this method will try to build a certificate chain using all
830      * available trust anchors (preinstalled and user-added).
831      *
832      * <p> This method may block while waiting for a connection to another process, and must never
833      * be called from the main thread.
834      * <p> As {@link Activity} and {@link Service} contexts are short-lived and can be destroyed
835      * at any time from the main thread, it is safer to rely on a long-lived context such as one
836      * returned from {@link Context#getApplicationContext()}.
837      * <p> In case the caller specifies an alias for which it lacks a grant, it must call
838      * {@link #choosePrivateKeyAlias} again. See {@link #getPrivateKey} for more details on
839      * coping with this scenario.
840      *
841      * @param alias The alias of the desired certificate chain, typically
842      * returned via {@link KeyChainAliasCallback#alias}.
843      * @throws KeyChainException if the alias was valid but there was some problem accessing it.
844      * @throws IllegalStateException if called from the main thread.
845      */
846     @Nullable @WorkerThread
getCertificateChain(@onNull Context context, @NonNull String alias)847     public static X509Certificate[] getCertificateChain(@NonNull Context context,
848             @NonNull String alias) throws KeyChainException, InterruptedException {
849         if (alias == null) {
850             throw new NullPointerException("alias == null");
851         }
852 
853         final byte[] certificateBytes;
854         final byte[] certChainBytes;
855         try (KeyChainConnection keyChainConnection = bind(context.getApplicationContext())) {
856             IKeyChainService keyChainService = keyChainConnection.getService();
857             certificateBytes = keyChainService.getCertificate(alias);
858             if (certificateBytes == null) {
859                 return null;
860             }
861             certChainBytes = keyChainService.getCaCertificates(alias);
862         } catch (RemoteException e) {
863             throw new KeyChainException(e);
864         } catch (RuntimeException e) {
865             // only certain RuntimeExceptions can be propagated across the IKeyChainService call
866             throw new KeyChainException(e);
867         }
868 
869         try {
870             X509Certificate leafCert = toCertificate(certificateBytes);
871             // If the keypair is installed with a certificate chain by either
872             // DevicePolicyManager.installKeyPair or CertInstaller, return that chain.
873             if (certChainBytes != null && certChainBytes.length != 0) {
874                 Collection<X509Certificate> chain = toCertificates(certChainBytes);
875                 ArrayList<X509Certificate> fullChain = new ArrayList<>(chain.size() + 1);
876                 fullChain.add(leafCert);
877                 fullChain.addAll(chain);
878                 return fullChain.toArray(new X509Certificate[fullChain.size()]);
879             } else {
880                 // If there isn't a certificate chain, either due to a pre-existing keypair
881                 // installed before N, or no chain is explicitly installed under the new logic,
882                 // fall back to old behavior of constructing the chain from trusted credentials.
883                 //
884                 // This logic exists to maintain old behaviour for already installed keypair, at
885                 // the cost of potentially returning extra certificate chain for new clients who
886                 // explicitly installed only the client certificate without a chain. The latter
887                 // case is actually no different from pre-N behaviour of getCertificateChain(),
888                 // in that sense this change introduces no regression. Besides the returned chain
889                 // is still valid so the consumer of the chain should have no problem verifying it.
890                 TrustedCertificateStore store = new TrustedCertificateStore();
891                 List<X509Certificate> chain = store.getCertificateChain(leafCert);
892                 return chain.toArray(new X509Certificate[chain.size()]);
893             }
894         } catch (CertificateException | RuntimeException e) {
895             throw new KeyChainException(e);
896         }
897     }
898 
899     /**
900      * Returns {@code true} if the current device's {@code KeyChain} supports a
901      * specific {@code PrivateKey} type indicated by {@code algorithm} (e.g.,
902      * "RSA").
903      */
isKeyAlgorithmSupported( @onNull @eyProperties.KeyAlgorithmEnum String algorithm)904     public static boolean isKeyAlgorithmSupported(
905             @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) {
906         final String algUpper = algorithm.toUpperCase(Locale.US);
907         return KeyProperties.KEY_ALGORITHM_EC.equals(algUpper)
908                 || KeyProperties.KEY_ALGORITHM_RSA.equals(algUpper);
909     }
910 
911     /**
912      * Returns {@code true} if the current device's {@code KeyChain} binds any
913      * {@code PrivateKey} of the given {@code algorithm} to the device once
914      * imported or generated. This can be used to tell if there is special
915      * hardware support that can be used to bind keys to the device in a way
916      * that makes it non-exportable.
917      *
918      * @deprecated Whether the key is bound to the secure hardware is known only
919      * once the key has been imported. To find out, use:
920      * <pre>{@code
921      * PrivateKey key = ...; // private key from KeyChain
922      *
923      * KeyFactory keyFactory =
924      *     KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
925      * KeyInfo keyInfo = keyFactory.getKeySpec(key, KeyInfo.class);
926      * if (keyInfo.isInsideSecureHardware()) {
927      *     // The key is bound to the secure hardware of this Android
928      * }}</pre>
929      */
930     @Deprecated
isBoundKeyAlgorithm( @onNull @eyProperties.KeyAlgorithmEnum String algorithm)931     public static boolean isBoundKeyAlgorithm(
932             @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) {
933         // All supported algorithms are hardware backed. Individual keys may not be.
934         return true;
935     }
936 
937     /** @hide */
938     @NonNull
toCertificate(@onNull byte[] bytes)939     public static X509Certificate toCertificate(@NonNull byte[] bytes) {
940         if (bytes == null) {
941             throw new IllegalArgumentException("bytes == null");
942         }
943         try {
944             CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
945             Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
946             return (X509Certificate) cert;
947         } catch (CertificateException e) {
948             throw new AssertionError(e);
949         }
950     }
951 
952     /** @hide */
953     @NonNull
toCertificates(@onNull byte[] bytes)954     public static Collection<X509Certificate> toCertificates(@NonNull byte[] bytes) {
955         if (bytes == null) {
956             throw new IllegalArgumentException("bytes == null");
957         }
958         try {
959             CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
960             return (Collection<X509Certificate>) certFactory.generateCertificates(
961                     new ByteArrayInputStream(bytes));
962         } catch (CertificateException e) {
963             throw new AssertionError(e);
964         }
965     }
966 
967     /**
968      * @hide for reuse by CertInstaller and Settings.
969      * @see KeyChain#bind
970      */
971     public static class KeyChainConnection implements Closeable {
972         private final Context mContext;
973         private final ServiceConnection mServiceConnection;
974         private final IKeyChainService mService;
KeyChainConnection(Context context, ServiceConnection serviceConnection, IKeyChainService service)975         protected KeyChainConnection(Context context,
976                                      ServiceConnection serviceConnection,
977                                      IKeyChainService service) {
978             this.mContext = context;
979             this.mServiceConnection = serviceConnection;
980             this.mService = service;
981         }
close()982         @Override public void close() {
983             mContext.unbindService(mServiceConnection);
984         }
985 
986         /** returns the service binder. */
getService()987         public IKeyChainService getService() {
988             return mService;
989         }
990     }
991 
992     /**
993      * Bind to KeyChainService in the current user.
994      * Caller should call unbindService on the result when finished.
995      *
996      *@throws InterruptedException if interrupted during binding.
997      *@throws AssertionError if unable to bind to KeyChainService.
998      * @hide for reuse by CertInstaller and Settings.
999      */
1000     @WorkerThread
bind(@onNull Context context)1001     public static KeyChainConnection bind(@NonNull Context context) throws InterruptedException {
1002         return bindAsUser(context, Process.myUserHandle());
1003     }
1004 
1005     /**
1006      * Bind to KeyChainService in the target user.
1007      * Caller should call unbindService on the result when finished.
1008      *
1009      * @throws InterruptedException if interrupted during binding.
1010      * @throws AssertionError if unable to bind to KeyChainService.
1011      * @hide
1012      */
1013     @WorkerThread
bindAsUser(@onNull Context context, UserHandle user)1014     public static KeyChainConnection bindAsUser(@NonNull Context context, UserHandle user)
1015             throws InterruptedException {
1016         return bindAsUser(context, null, user);
1017     }
1018 
1019     /**
1020      * Returns a persistable grant string that allows WiFi stack to access the key using Keystore
1021      * SSL engine.
1022      *
1023      * @return grant string or null if key is not granted or doesn't exist.
1024      *
1025      * The key should be granted to Process.WIFI_UID.
1026      * @hide
1027      */
1028     @SystemApi
1029     @Nullable
1030     @WorkerThread
getWifiKeyGrantAsUser( @onNull Context context, @NonNull UserHandle user, @NonNull String alias)1031     public static String getWifiKeyGrantAsUser(
1032             @NonNull Context context, @NonNull UserHandle user, @NonNull String alias) {
1033         try (KeyChainConnection keyChainConnection =
1034                      bindAsUser(context.getApplicationContext(), user)) {
1035             return keyChainConnection.getService().getWifiKeyGrantAsUser(alias);
1036         } catch (RemoteException | RuntimeException e) {
1037             Log.i(LOG, "Couldn't get grant for wifi", e);
1038             return null;
1039         } catch (InterruptedException e) {
1040             Thread.currentThread().interrupt();
1041             Log.i(LOG, "Interrupted while getting grant for wifi", e);
1042             return null;
1043         }
1044     }
1045 
1046     /**
1047      * Returns whether the key is granted to WiFi stack.
1048      * @hide
1049      */
1050     @SystemApi
1051     @WorkerThread
hasWifiKeyGrantAsUser( @onNull Context context, @NonNull UserHandle user, @NonNull String alias)1052     public static boolean hasWifiKeyGrantAsUser(
1053             @NonNull Context context, @NonNull UserHandle user, @NonNull String alias) {
1054         try (KeyChainConnection keyChainConnection =
1055                      bindAsUser(context.getApplicationContext(), user)) {
1056             return keyChainConnection.getService().hasGrant(Process.WIFI_UID, alias);
1057         } catch (RemoteException | RuntimeException e) {
1058             Log.i(LOG, "Couldn't query grant for wifi", e);
1059             return false;
1060         } catch (InterruptedException e) {
1061             Thread.currentThread().interrupt();
1062             Log.i(LOG, "Interrupted while querying grant for wifi", e);
1063             return false;
1064         }
1065     }
1066 
1067     /**
1068      * Bind to KeyChainService in the target user.
1069      * Caller should call unbindService on the result when finished.
1070      *
1071      * @throws InterruptedException if interrupted during binding.
1072      * @throws AssertionError if unable to bind to KeyChainService.
1073      * @hide
1074      */
bindAsUser(@onNull Context context, @Nullable Handler handler, UserHandle user)1075     public static KeyChainConnection bindAsUser(@NonNull Context context, @Nullable Handler handler,
1076             UserHandle user) throws InterruptedException {
1077 
1078         if (context == null) {
1079             throw new NullPointerException("context == null");
1080         }
1081         if (handler == null) {
1082             ensureNotOnMainThread(context);
1083         }
1084         if (!UserManager.get(context).isUserUnlocked(user)) {
1085             throw new IllegalStateException("User must be unlocked");
1086         }
1087 
1088         final CountDownLatch countDownLatch = new CountDownLatch(1);
1089         final AtomicReference<IKeyChainService> keyChainService = new AtomicReference<>();
1090         ServiceConnection keyChainServiceConnection = new ServiceConnection() {
1091             volatile boolean mConnectedAtLeastOnce = false;
1092             @Override public void onServiceConnected(ComponentName name, IBinder service) {
1093                 if (!mConnectedAtLeastOnce) {
1094                     mConnectedAtLeastOnce = true;
1095                     keyChainService.set(
1096                             IKeyChainService.Stub.asInterface(Binder.allowBlocking(service)));
1097                     countDownLatch.countDown();
1098                 }
1099             }
1100             @Override public void onBindingDied(ComponentName name) {
1101                 if (!mConnectedAtLeastOnce) {
1102                     mConnectedAtLeastOnce = true;
1103                     countDownLatch.countDown();
1104                 }
1105             }
1106             @Override public void onServiceDisconnected(ComponentName name) {}
1107         };
1108         Intent intent = new Intent(IKeyChainService.class.getName());
1109         ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
1110         if (comp == null) {
1111             throw new AssertionError("could not resolve KeyChainService");
1112         }
1113         intent.setComponent(comp);
1114         final boolean bindSucceed;
1115         if (handler != null) {
1116             bindSucceed = context.bindServiceAsUser(
1117                     intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, handler, user);
1118         } else {
1119             bindSucceed = context.bindServiceAsUser(
1120                     intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, user);
1121         }
1122         if (!bindSucceed) {
1123             context.unbindService(keyChainServiceConnection);
1124             throw new AssertionError("could not bind to KeyChainService");
1125         }
1126         if (!countDownLatch.await(BIND_KEY_CHAIN_SERVICE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
1127             context.unbindService(keyChainServiceConnection);
1128             throw new AssertionError("binding to KeyChainService timeout");
1129         }
1130         IKeyChainService service = keyChainService.get();
1131         if (service != null) {
1132             return new KeyChainConnection(context, keyChainServiceConnection, service);
1133         } else {
1134             context.unbindService(keyChainServiceConnection);
1135             throw new AssertionError("KeyChainService died while binding");
1136         }
1137     }
1138 
ensureNotOnMainThread(@onNull Context context)1139     private static void ensureNotOnMainThread(@NonNull Context context) {
1140         Looper looper = Looper.myLooper();
1141         if (looper != null && looper == context.getMainLooper()) {
1142             throw new IllegalStateException(
1143                     "calling this from your main thread can lead to deadlock");
1144         }
1145     }
1146 }
1147