• 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 
17 package com.android.keychain;
18 
19 import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_INSTALLED;
20 import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_REMOVED;
21 import static android.security.keystore.KeyProperties.UID_SELF;
22 
23 import android.Manifest;
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.app.AppOpsManager;
27 import android.app.BroadcastOptions;
28 import android.app.IntentService;
29 import android.app.admin.SecurityLog;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.pm.ApplicationInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.StringParceledListSlice;
35 import android.hardware.security.keymint.ErrorCode;
36 import android.net.Uri;
37 import android.os.Binder;
38 import android.os.Build;
39 import android.os.IBinder;
40 import android.os.UserManager;
41 import android.os.Process;
42 import android.os.UserHandle;
43 import android.security.AppUriAuthenticationPolicy;
44 import android.security.CredentialManagementApp;
45 import android.security.IKeyChainService;
46 import android.security.KeyChain;
47 import android.security.KeyStore2;
48 import android.security.keystore.KeyGenParameterSpec;
49 import android.security.keystore.KeyProperties;
50 import android.security.keystore.ParcelableKeyGenParameterSpec;
51 import android.security.keystore.StrongBoxUnavailableException;
52 import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
53 import android.system.keystore2.Domain;
54 import android.system.keystore2.KeyDescriptor;
55 import android.system.keystore2.KeyPermission;
56 import android.text.TextUtils;
57 import android.util.Base64;
58 import android.util.Log;
59 
60 import com.android.internal.annotations.GuardedBy;
61 import com.android.internal.annotations.VisibleForTesting;
62 import com.android.internal.util.Preconditions;
63 import com.android.keychain.internal.ExistingKeysProvider;
64 import com.android.keychain.internal.GrantsDatabase;
65 import com.android.org.conscrypt.TrustedCertificateStore;
66 
67 import java.io.ByteArrayInputStream;
68 import java.io.ByteArrayOutputStream;
69 import java.io.IOException;
70 import java.security.InvalidAlgorithmParameterException;
71 import java.security.Key;
72 import java.security.KeyFactory;
73 import java.security.KeyPair;
74 import java.security.KeyPairGenerator;
75 import java.security.KeyStore;
76 import java.security.KeyStoreException;
77 import java.security.NoSuchAlgorithmException;
78 import java.security.NoSuchProviderException;
79 import java.security.PrivateKey;
80 import java.security.ProviderException;
81 import java.security.UnrecoverableKeyException;
82 import java.security.cert.Certificate;
83 import java.security.cert.CertificateEncodingException;
84 import java.security.cert.CertificateException;
85 import java.security.cert.CertificateFactory;
86 import java.security.cert.X509Certificate;
87 import java.security.spec.InvalidKeySpecException;
88 import java.security.spec.PKCS8EncodedKeySpec;
89 import java.util.ArrayList;
90 import java.util.Arrays;
91 import java.util.Collection;
92 import java.util.Collections;
93 import java.util.Enumeration;
94 import java.util.HashSet;
95 import java.util.List;
96 import java.util.Map;
97 import java.util.Set;
98 
99 import javax.security.auth.x500.X500Principal;
100 
101 public class KeyChainService extends IntentService {
102 
103     private static final String TAG = "KeyChain";
104     private static final String CERT_INSTALLER_PACKAGE = "com.android.certinstaller";
105     private final Set<Integer> ALLOWED_UIDS = Collections.unmodifiableSet(
106             new HashSet(Arrays.asList(UID_SELF, Process.WIFI_UID)));
107 
108     private static final String MSG_NOT_SYSTEM = "Not system package";
109     private static final String MSG_NOT_SYSTEM_OR_CERT_INSTALLER =
110             "Not system or cert installer package";
111     private static final String MSG_NOT_SYSTEM_OR_CRED_MNG_APP =
112             "Not system or credential management app package";
113 
114     /** created in onCreate(), closed in onDestroy() */
115     private GrantsDatabase mGrantsDb;
116     private Injector mInjector;
117     private KeyStore mKeyStore;
118     private KeyChainStateStorage mStateStorage;
119 
120     private Object mCredentialManagementAppLock = new Object();
121     @Nullable
122     @GuardedBy("mCredentialManagementAppLock")
123     private CredentialManagementApp mCredentialManagementApp;
124 
KeyChainService()125     public KeyChainService() {
126         super(KeyChainService.class.getSimpleName());
127         mInjector = new Injector();
128     }
129 
getKeyStore()130     private KeyStore getKeyStore() {
131         try {
132             final KeyStore keystore = mInjector.getKeyStoreInstance();
133             keystore.load(null);
134             return keystore;
135         } catch (KeyStoreException | IOException | NoSuchAlgorithmException
136                 | CertificateException e) {
137             Log.e(TAG, "Error opening AndroidKeyStore.", e);
138             throw new RuntimeException("Error opening AndroidKeyStore.", e);
139         }
140     }
141 
getKeyStore(boolean useWifiNamespace)142     private KeyStore getKeyStore(boolean useWifiNamespace) {
143         if (!useWifiNamespace) {
144             return mKeyStore;
145         }
146         try {
147             final KeyStore keystore = mInjector.getKeyStoreInstance();
148             keystore.load(
149                     new AndroidKeyStoreLoadStoreParameter(
150                             KeyProperties.NAMESPACE_WIFI));
151             return keystore;
152         } catch (IOException | CertificateException | KeyStoreException
153                 | NoSuchAlgorithmException e) {
154             Log.e(TAG, "Failed to open AndroidKeyStore for WI-FI namespace.", e);
155             return null;
156         }
157     }
158 
onCreate()159     @Override public void onCreate() {
160         super.onCreate();
161         mKeyStore = getKeyStore();
162         mGrantsDb = new GrantsDatabase(this, new KeyStoreAliasesProvider(mKeyStore));
163         mStateStorage = new KeyChainStateStorage(getDataDir());
164 
165         synchronized (mCredentialManagementAppLock) {
166             mCredentialManagementApp = mStateStorage.loadCredentialManagementApp();
167         }
168     }
169 
170     @Override
onDestroy()171     public void onDestroy() {
172         super.onDestroy();
173         mGrantsDb.destroy();
174         mGrantsDb = null;
175     }
176 
177     private static class KeyStoreAliasesProvider implements ExistingKeysProvider {
178         private final KeyStore mKeyStore;
179 
KeyStoreAliasesProvider(KeyStore keyStore)180         KeyStoreAliasesProvider(KeyStore keyStore) {
181             mKeyStore = keyStore;
182         }
183 
184         @Override
getExistingKeyAliases()185         public List<String> getExistingKeyAliases() {
186             final List<String> keyStoreAliases = new ArrayList<>();
187             try {
188                 final Enumeration<String> aliases = mKeyStore.aliases();
189                 while (aliases.hasMoreElements()) {
190                     final String alias = aliases.nextElement();
191                     if (mKeyStore.isKeyEntry(alias)) {
192                         keyStoreAliases.add(alias);
193                     }
194                 }
195             } catch (KeyStoreException e) {
196                 Log.e(TAG, "Error while loading entries from keystore. "
197                         + "List may be empty or incomplete.");
198             }
199 
200             return keyStoreAliases;
201         }
202     }
203 
makeKeyDescriptor(String alias)204     private KeyDescriptor makeKeyDescriptor(String alias) {
205         final KeyDescriptor key = new KeyDescriptor();
206         key.domain = Domain.APP;
207         key.nspace = KeyProperties.NAMESPACE_APPLICATION;
208         key.alias = alias;
209         key.blob = null;
210         return key;
211     }
212 
213     private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() {
214         private final TrustedCertificateStore mTrustedCertificateStore
215                 = new TrustedCertificateStore();
216         private final Context mContext = KeyChainService.this;
217 
218         @Override
219         public String requestPrivateKey(String alias) {
220             final CallerIdentity caller = getCaller();
221             if (!hasGrant(alias, caller)) {
222                 return null;
223             }
224 
225             final int granteeUid = caller.mUid;
226 
227             try {
228                 final KeyStore2 keyStore2 = KeyStore2.getInstance();
229                 KeyDescriptor grant = keyStore2.grant(makeKeyDescriptor(alias), granteeUid,
230                         KeyPermission.USE | KeyPermission.GET_INFO);
231                 return KeyChain.getGrantString(grant);
232             } catch (android.security.KeyStoreException e) {
233                 Log.e(TAG, "Failed to grant " + alias + " to uid: " + granteeUid, e);
234                 return null;
235             }
236         }
237 
238         @Override
239         public String getWifiKeyGrantAsUser(String alias) {
240             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
241 
242             if (!hasGrant(alias, Process.WIFI_UID)) {
243                 return null;
244             }
245 
246             KeyStore2 keyStore2 = KeyStore2.getInstance();
247             try {
248                 KeyDescriptor grant = keyStore2.grant(makeKeyDescriptor(alias),
249                         Process.WIFI_UID, KeyPermission.USE | KeyPermission.GET_INFO);
250                 return KeyStore2.makeKeystoreEngineGrantString(grant.nspace);
251             } catch (android.security.KeyStoreException e) {
252                 Log.e(TAG, "Failed to grant " + alias + " to uid: " + Process.WIFI_UID, e);
253                 return null;
254             }
255         }
256 
257         @Override public byte[] getCertificate(String alias) {
258             final CallerIdentity caller = getCaller();
259             if (!hasGrant(alias, caller) && !isSystemUid(caller)) {
260                 return null;
261             }
262             try {
263                 if (!mKeyStore.isCertificateEntry(alias)) {
264                     final Certificate cert = mKeyStore.getCertificate(alias);
265                     if (cert == null) return null;
266                     return cert.getEncoded();
267                 } else {
268                     return null;
269                 }
270             } catch (KeyStoreException | CertificateEncodingException e) {
271                 Log.e(TAG, "Failed to retrieve certificate.", e);
272                 return null;
273             }
274         }
275 
276         @Override public byte[] getCaCertificates(String alias) {
277             final CallerIdentity caller = getCaller();
278             if (!hasGrant(alias, caller) && !isSystemUid(caller)) {
279                 return null;
280             }
281             try {
282                 if (mKeyStore.isCertificateEntry(alias)) {
283                     return mKeyStore.getCertificate(alias).getEncoded();
284                 } else {
285                     final Certificate[] certs = mKeyStore.getCertificateChain(alias);
286                     if (certs == null || certs.length <= 1) return null;
287                     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
288                     for (int i = 1; i < certs.length; ++i) {
289                         outputStream.write(certs[i].getEncoded());
290                     }
291                     return outputStream.toByteArray();
292                 }
293             } catch (KeyStoreException | CertificateEncodingException | IOException e) {
294                 Log.e(TAG, "Failed to retrieve certificate(s) from AndroidKeyStore.", e);
295                 return null;
296             }
297         }
298 
299         @Override public boolean isUserSelectable(String alias) {
300             validateAlias(alias);
301             return mGrantsDb.isUserSelectable(alias);
302         }
303 
304         @Override public void setUserSelectable(String alias, boolean isUserSelectable) {
305             validateAlias(alias);
306             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
307             Log.i(TAG, String.format("Marking certificate %s as user-selectable: %b", alias,
308                     isUserSelectable));
309             mGrantsDb.setIsUserSelectable(alias, isUserSelectable);
310         }
311 
312         @Override public int generateKeyPair(
313                 String algorithm, ParcelableKeyGenParameterSpec parcelableSpec) {
314             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
315             final KeyGenParameterSpec spec = parcelableSpec.getSpec();
316             final String alias = spec.getKeystoreAlias();
317 
318             Log.i(TAG, String.format("About to generate key with alias %s, algorithm %s",
319                     alias, algorithm));
320 
321             if (KeyChain.KEY_ALIAS_SELECTION_DENIED.equals(alias)) {
322                 throw new IllegalArgumentException("The alias specified for the key denotes "
323                         + "a reserved value and cannot be used to name a key");
324             }
325             // Validate the alias here to avoid relying on KeyGenParameterSpec c'tor preventing
326             // the creation of a KeyGenParameterSpec instance with a non-empty alias.
327             if (TextUtils.isEmpty(alias) || spec.getUid() != UID_SELF) {
328                 Log.e(TAG, "Cannot generate key pair with empty alias or specified uid.");
329                 return KeyChain.KEY_GEN_MISSING_ALIAS;
330             }
331 
332             try {
333                 KeyPairGenerator generator = KeyPairGenerator.getInstance(
334                         algorithm, "AndroidKeyStore");
335                 // Do not prepend USER_PRIVATE_KEY to the alias because
336                 // AndroidKeyStoreKeyPairGeneratorSpi will helpfully prepend that in
337                 // generateKeyPair.
338                 generator.initialize(spec);
339                 KeyPair kp = generator.generateKeyPair();
340                 if (kp == null) {
341                     Log.e(TAG, "Key generation failed.");
342                     return KeyChain.KEY_GEN_FAILURE;
343                 }
344                 return KeyChain.KEY_GEN_SUCCESS;
345             } catch (NoSuchAlgorithmException e) {
346                 Log.e(TAG, "Invalid algorithm requested", e);
347                 return KeyChain.KEY_GEN_NO_SUCH_ALGORITHM;
348             } catch (InvalidAlgorithmParameterException e) {
349                 Log.e(TAG, "Invalid algorithm params", e);
350                 return KeyChain.KEY_GEN_INVALID_ALGORITHM_PARAMETERS;
351             } catch (NoSuchProviderException e) {
352                 Log.e(TAG, "Could not find Keystore.", e);
353                 return KeyChain.KEY_GEN_NO_KEYSTORE_PROVIDER;
354             } catch (StrongBoxUnavailableException e) {
355                 Log.e(TAG, "StrongBox unavailable.", e);
356                 return KeyChain.KEY_GEN_STRONGBOX_UNAVAILABLE;
357             } catch (ProviderException e) {
358                 Throwable t = e.getCause();
359                 if (t instanceof android.security.KeyStoreException) {
360                     if (((android.security.KeyStoreException) t).getErrorCode()
361                             == ErrorCode.CANNOT_ATTEST_IDS) {
362                         return KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS;
363                     }
364                 }
365                 Log.e(TAG, "KeyStore error.", e);
366                 return KeyChain.KEY_GEN_FAILURE;
367             }
368         }
369 
370         @Override public boolean setKeyPairCertificate(String alias, byte[] userCertificate,
371                 byte[] userCertificateChain) {
372             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
373 
374             final PrivateKey privateKey;
375             try {
376                 final Key key = mKeyStore.getKey(alias, null);
377                 if (! (key instanceof PrivateKey)) {
378                     return false;
379                 }
380                 privateKey = (PrivateKey) key;
381             } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
382                 Log.e(TAG, "Failed to get private key entry.", e);
383                 return false;
384             }
385 
386             final ArrayList<Certificate> certs = new ArrayList<>();
387             try {
388                 if (userCertificate != null) {
389                     certs.add(parseCertificate(userCertificate));
390                 }
391                 if (userCertificateChain != null) {
392                     certs.addAll(parseCertificates(userCertificateChain));
393                 }
394             } catch (CertificateException e) {
395                 Log.e(TAG, "Failed to parse user certificate.", e);
396                 return false;
397             }
398 
399             final Certificate[] certsArray = certs.toArray(new Certificate[0]);
400 
401             try {
402                 // setKeyEntry with a private key loaded from AndroidKeyStore replaces
403                 // the certificate components without touching the private key if
404                 // the alias is the same as that of the private key.
405                 mKeyStore.setKeyEntry(alias, privateKey, null, certsArray);
406             } catch (KeyStoreException e) {
407                 Log.e(TAG, "Failed update key certificates.", e);
408                 return false;
409             }
410 
411             if (Log.isLoggable(TAG, Log.DEBUG)) {
412                 Log.d(TAG, String.format("Set certificate for key alias %s : user %s CA chain: %s",
413                         alias, emptyOrBase64Encoded(userCertificate),
414                         emptyOrBase64Encoded(userCertificateChain)));
415             }
416             broadcastKeychainChange();
417             broadcastLegacyStorageChange();
418             return true;
419         }
420 
421         private void validateAlias(String alias) {
422             if (alias == null) {
423                 throw new NullPointerException("alias == null");
424             }
425         }
426 
427         private boolean hasGrant(String alias, CallerIdentity caller) {
428             return hasGrant(alias, caller.mUid);
429         }
430 
431         private boolean hasGrant(String alias, int targetUid) {
432             validateAlias(alias);
433 
434             if (!mGrantsDb.hasGrant(targetUid, alias)) {
435                 Log.w(TAG, String.format(
436                         "uid %d doesn't have permission to access the requested alias %s",
437                         targetUid, alias));
438                 return false;
439             }
440 
441             return true;
442         }
443 
444         @Override public String installCaCertificate(byte[] caCertificate) {
445             final CallerIdentity caller = getCaller();
446             Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller),
447                     MSG_NOT_SYSTEM_OR_CERT_INSTALLER);
448             final String alias;
449             String subject = null;
450             final boolean isSecurityLoggingEnabled = mInjector.isSecurityLoggingEnabled();
451             final X509Certificate cert;
452             try {
453                 cert = parseCertificate(caCertificate);
454 
455                 final boolean isDebugLoggable = Log.isLoggable(TAG, Log.DEBUG);
456                 subject = cert.getSubjectX500Principal().getName(X500Principal.CANONICAL);
457                 if (isDebugLoggable) {
458                     Log.d(TAG, String.format("Installing CA certificate: %s", subject));
459                 }
460 
461                 synchronized (mTrustedCertificateStore) {
462                     mTrustedCertificateStore.installCertificate(cert);
463                     alias = mTrustedCertificateStore.getCertificateAlias(cert);
464                 }
465             } catch (IOException | CertificateException e) {
466                 Log.w(TAG, "Failed installing CA certificate", e);
467                 if (isSecurityLoggingEnabled && subject != null) {
468                     mInjector.writeSecurityEvent(
469                             TAG_CERT_AUTHORITY_INSTALLED, 0 /*result*/, subject,
470                             UserHandle.myUserId());
471                 }
472                 throw new IllegalStateException(e);
473             }
474             if (isSecurityLoggingEnabled && subject != null) {
475                 mInjector.writeSecurityEvent(
476                         TAG_CERT_AUTHORITY_INSTALLED, 1 /*result*/, subject,
477                         UserHandle.myUserId());
478             }
479 
480             // If the caller is the cert installer, install the CA certificate into KeyStore.
481             // This is a temporary solution to enable CA certificates to be used as VPN trust
482             // anchors. Ultimately, the user should explicitly choose to install the VPN trust
483             // anchor separately and independently of CA certificates, at which point this code
484             // should be removed.
485             if (CERT_INSTALLER_PACKAGE.equals(caller.mPackageName)) {
486                 try {
487                     mKeyStore.setCertificateEntry(String.format("%s %s", subject, alias), cert);
488                 } catch(KeyStoreException e) {
489                     Log.e(TAG, String.format(
490                             "Attempted installing %s (subject: %s) to KeyStore. Failed", alias,
491                             subject), e);
492                 }
493             }
494 
495             broadcastLegacyStorageChange();
496             broadcastTrustStoreChange();
497             return alias;
498         }
499 
500         /**
501          * Install a key pair to the keystore.
502          *
503          * @param privateKey The private key associated with the client certificate
504          * @param userCertificate The client certificate to be installed
505          * @param userCertificateChain The rest of the chain for the client certificate
506          * @param alias The alias under which the key pair is installed. It is invalid to pass
507          *              {@code KeyChain.KEY_ALIAS_SELECTION_DENIED}.
508          * @param uid Can be only one of two values: Either
509          *            {@code android.security.keystore.KeyProperties.UID_SELF} to indicate
510          *            installation into the current user's system Keystore instance, or {@code
511          *            Process.WIFI_UID} to indicate installation into the main user's WiFi Keystore
512          *            instance. Only admin users are allowed to pass {@code Process.WIFI_UID} to
513          *            the KeyChain service.
514          * @return Whether the operation succeeded or not.
515          */
516         @Override public boolean installKeyPair(@Nullable byte[] privateKey,
517                 @Nullable byte[] userCertificate, @Nullable byte[] userCertificateChain,
518                 String alias, int uid) {
519             final CallerIdentity caller = getCaller();
520             Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller),
521                     MSG_NOT_SYSTEM_OR_CERT_INSTALLER);
522             if (KeyChain.KEY_ALIAS_SELECTION_DENIED.equals(alias)) {
523                 throw new IllegalArgumentException("The alias specified for the key denotes "
524                         + "a reserved value and cannot be used to name a key");
525             }
526             if (!ALLOWED_UIDS.contains(uid)) {
527                 Log.e(TAG,
528                         String.format("Installing alias %s as UID %d is now allowed.", alias, uid));
529                 return false;
530             }
531 
532             if (privateKey == null && userCertificate == null && userCertificateChain == null) {
533                 Log.e(TAG, String.format("Nothing to install for alias %s", alias));
534                 return false;
535             }
536 
537             UserManager userManager = mContext.getSystemService(UserManager.class);
538             if (uid == Process.WIFI_UID && !userManager.isAdminUser()) {
539                 Log.e(TAG,
540                     "Installation into the WiFi Keystore should be called from the admin user");
541                 return false;
542             }
543 
544             if (Log.isLoggable(TAG, Log.DEBUG)) {
545                 Log.d(TAG, String.format("Installing certificate and key to alias %s to uid %d: "
546                                 + "user cert %s CA chain: %s", alias, uid,
547                                 emptyOrBase64Encoded(userCertificate),
548                                 emptyOrBase64Encoded(userCertificateChain)));
549             }
550 
551             final ArrayList<Certificate> certs = new ArrayList<>();
552             try {
553                 if (userCertificate != null) {
554                     certs.add(parseCertificate(userCertificate));
555                 }
556                 if (userCertificateChain != null) {
557                     certs.addAll(parseCertificates(userCertificateChain));
558                 }
559             } catch (CertificateException e) {
560                 Log.e(TAG, "Failed to parse user certificate.", e);
561                 return false;
562             }
563 
564             if (certs.isEmpty()) {
565                 Log.e(TAG, "Cannot install private key without public certificate.");
566                 return false;
567             }
568 
569             final Certificate[] certificates = certs.toArray(new Certificate[0]);
570 
571             final PrivateKey privateKey1;
572             try {
573                 if (privateKey != null) {
574                     final KeyFactory keyFactory =
575                             KeyFactory.getInstance(certificates[0].getPublicKey().getAlgorithm());
576                     privateKey1 = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
577                 } else {
578                     privateKey1 = null;
579                 }
580             } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
581                 Log.e(TAG, "Failed to parse private key.", e);
582                 return false;
583             }
584 
585             KeyStore keystore = getKeyStore(uid == Process.WIFI_UID);
586             if (keystore == null) {
587                 return false;
588             }
589 
590             try {
591                 if (privateKey != null) {
592                     keystore.setKeyEntry(alias, privateKey1, null, certificates);
593                 } else {
594                     if (certificates.length > 1) {
595                         Log.e(TAG,
596                                 "Cannot install key certificate chain without private key.");
597                         return false;
598                     }
599                     keystore.setCertificateEntry(alias, certificates[0]);
600                 }
601             } catch (KeyStoreException e) {
602                 Log.e(TAG, "Failed to install key pair.", e);
603             }
604 
605             broadcastKeychainChange();
606             broadcastLegacyStorageChange();
607             return true;
608         }
609 
610         @Override public boolean removeKeyPair(String alias) {
611             final CallerIdentity caller = getCaller();
612             Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller),
613                     MSG_NOT_SYSTEM_OR_CERT_INSTALLER);
614             return removeKeyPairInternal(alias);
615         }
616 
617         private boolean removeKeyPairInternal(String alias) {
618             try {
619                 mKeyStore.deleteEntry(alias);
620             } catch (KeyStoreException e) {
621                 Log.e(TAG, String.format(
622                         "Failed not remove keystore entry with alias %s", alias));
623                 return false;
624             }
625             Log.w(TAG, String.format(
626                     "WARNING: Removing alias %s, existing grants will be revoked.", alias));
627             mGrantsDb.removeAliasInformation(alias);
628             broadcastKeychainChange();
629             broadcastLegacyStorageChange();
630             return true;
631         }
632 
633         @Override public boolean containsKeyPair(String alias) {
634             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
635             try {
636                 final Key key = mKeyStore.getKey(alias, null);
637                 return key instanceof PrivateKey;
638             } catch (UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e) {
639                 Log.w("Error while trying to check for key presence.", e);
640                 return false;
641             }
642         }
643 
644         private X509Certificate parseCertificate(byte[] bytes) throws CertificateException {
645             CertificateFactory cf = CertificateFactory.getInstance("X.509");
646             return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(bytes));
647         }
648         private Collection<X509Certificate> parseCertificates(byte[] bytes)
649                 throws CertificateException {
650             final CertificateFactory cf = CertificateFactory.getInstance("X.509");
651             return (Collection<X509Certificate>)
652                     cf.generateCertificates(new ByteArrayInputStream(bytes));
653         }
654 
655         @Override public boolean reset() {
656             // only Settings should be able to reset
657             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
658             mGrantsDb.removeAllAliasesInformation();
659             boolean ok = true;
660             synchronized (mTrustedCertificateStore) {
661                 // delete user-installed CA certs
662                 for (String alias : mTrustedCertificateStore.aliases()) {
663                     if (TrustedCertificateStore.isUser(alias)) {
664                         if (!deleteCertificateEntry(alias)) {
665                             ok = false;
666                         }
667                     }
668                 }
669             }
670             broadcastTrustStoreChange();
671             broadcastKeychainChange();
672             broadcastLegacyStorageChange();
673             return ok;
674         }
675 
676         @Override public boolean deleteCaCertificate(String alias) {
677             // only Settings should be able to delete
678             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
679             boolean ok = true;
680             Log.i(TAG, String.format("Deleting CA certificate %s", alias));
681             synchronized (mTrustedCertificateStore) {
682                 ok = deleteCertificateEntry(alias);
683             }
684             broadcastTrustStoreChange();
685             broadcastLegacyStorageChange();
686             return ok;
687         }
688 
689         private boolean deleteCertificateEntry(String alias) {
690             String subjectForAudit = null;
691             if (mInjector.isSecurityLoggingEnabled()) {
692                 final Certificate cert = mTrustedCertificateStore.getCertificate(alias);
693                 if (cert instanceof X509Certificate) {
694                     subjectForAudit = ((X509Certificate) cert)
695                             .getSubjectX500Principal().getName(X500Principal.CANONICAL);
696                 }
697             }
698 
699             try {
700                 mTrustedCertificateStore.deleteCertificateEntry(alias);
701                 if (subjectForAudit != null) {
702                     mInjector.writeSecurityEvent(
703                             TAG_CERT_AUTHORITY_REMOVED, 1 /*result*/, subjectForAudit,
704                             UserHandle.myUserId());
705                 }
706                 return true;
707             } catch (IOException | CertificateException e) {
708                 Log.w(TAG, "Problem removing CA certificate " + alias, e);
709                 if (subjectForAudit != null) {
710                     mInjector.writeSecurityEvent(
711                             TAG_CERT_AUTHORITY_REMOVED, 0 /*result*/, subjectForAudit,
712                             UserHandle.myUserId());
713                 }
714                 return false;
715             }
716         }
717 
718         private boolean hasManageCredentialManagementAppPermission(CallerIdentity caller) {
719             return mContext.checkPermission(Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP,
720                     caller.mPid, caller.mUid) == PackageManager.PERMISSION_GRANTED;
721         }
722 
723         private boolean isCertInstaller(CallerIdentity caller) {
724             return caller.mPackageName != null
725                     && CERT_INSTALLER_PACKAGE.equals(caller.mPackageName);
726         }
727 
728         private boolean isCredentialManagementApp(CallerIdentity caller) {
729             synchronized (mCredentialManagementAppLock) {
730                 return mCredentialManagementApp != null && caller.mPackageName != null
731                         && caller.mPackageName.equals(mCredentialManagementApp.getPackageName());
732             }
733         }
734 
735         private boolean isSystemUid(CallerIdentity caller) {
736             return UserHandle.isSameApp(caller.mUid, Process.SYSTEM_UID);
737         }
738 
739         @Override public boolean hasGrant(int uid, String alias) {
740             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
741             return mGrantsDb.hasGrant(uid, alias);
742         }
743 
744         @Override public boolean setGrant(int uid, String alias, boolean granted) {
745             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
746             Preconditions.checkArgument(containsKeyPair(alias),
747                     "Alias not associated with a key.");
748             mGrantsDb.setGrant(uid, alias, granted);
749             if (!granted) {
750                 try {
751                     KeyStore2.getInstance().ungrant(makeKeyDescriptor(alias), uid);
752                 } catch (android.security.KeyStoreException e) {
753                     Log.e(TAG, "Failed to ungrant " + alias + " to uid: " + uid, e);
754                     return false;
755                 }
756             }
757             broadcastPermissionChange(uid, alias, granted);
758             broadcastLegacyStorageChange();
759             return true;
760         }
761 
762         @Override public int[] getGrants(String alias) {
763             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
764             try {
765                 if (mKeyStore.isKeyEntry(alias)) {
766                     return mGrantsDb.getGrants(alias);
767                 }
768             } catch (KeyStoreException e) {
769                 Log.w(TAG, "Error while checking if key exists.", e);
770             }
771             throw new IllegalArgumentException("Alias not found: " + alias);
772         }
773 
774         @Override
775         public StringParceledListSlice getUserCaAliases() {
776             synchronized (mTrustedCertificateStore) {
777                 return new StringParceledListSlice(new ArrayList<String>(
778                         mTrustedCertificateStore.userAliases()));
779             }
780         }
781 
782         @Override
783         public StringParceledListSlice getSystemCaAliases() {
784             synchronized (mTrustedCertificateStore) {
785                 return new StringParceledListSlice(new ArrayList<String>(
786                         mTrustedCertificateStore.allSystemAliases()));
787             }
788         }
789 
790         @Override
791         public boolean containsCaAlias(String alias) {
792             return mTrustedCertificateStore.containsAlias(alias);
793         }
794 
795         @Override
796         public byte[] getEncodedCaCertificate(String alias, boolean includeDeletedSystem) {
797             synchronized (mTrustedCertificateStore) {
798                 X509Certificate certificate = (X509Certificate) mTrustedCertificateStore
799                         .getCertificate(alias, includeDeletedSystem);
800                 if (certificate == null) {
801                     Log.w(TAG, "Could not find CA certificate " + alias);
802                     return null;
803                 }
804                 try {
805                     return certificate.getEncoded();
806                 } catch (CertificateEncodingException e) {
807                     Log.w(TAG, "Error while encoding CA certificate " + alias);
808                     return null;
809                 }
810             }
811         }
812 
813         @Override
814         public List<String> getCaCertificateChainAliases(String rootAlias,
815                 boolean includeDeletedSystem) {
816             synchronized (mTrustedCertificateStore) {
817                 X509Certificate root = (X509Certificate) mTrustedCertificateStore.getCertificate(
818                         rootAlias, includeDeletedSystem);
819                 try {
820                     List<X509Certificate> chain = mTrustedCertificateStore.getCertificateChain(
821                             root);
822                     List<String> aliases = new ArrayList<String>(chain.size());
823                     final int n = chain.size();
824                     for (int i = 0; i < n; ++i) {
825                         String alias = mTrustedCertificateStore.getCertificateAlias(chain.get(i),
826                                 true);
827                         if (alias != null) {
828                             aliases.add(alias);
829                         }
830                     }
831                     return aliases;
832                 } catch (CertificateException e) {
833                     Log.w(TAG, "Error retrieving cert chain for root " + rootAlias);
834                     return Collections.emptyList();
835                 }
836             }
837         }
838 
839         @Override
840         public void setCredentialManagementApp(@NonNull String packageName,
841                 @NonNull AppUriAuthenticationPolicy authenticationPolicy) {
842             final CallerIdentity caller = getCaller();
843             Preconditions.checkCallAuthorization(isSystemUid(caller)
844                     || hasManageCredentialManagementAppPermission(caller), MSG_NOT_SYSTEM);
845             checkValidAuthenticationPolicy(authenticationPolicy);
846 
847             synchronized (mCredentialManagementAppLock) {
848                 if (mCredentialManagementApp != null) {
849                     final String existingPackage = mCredentialManagementApp.getPackageName();
850                     if (existingPackage.equals(packageName)) {
851                         // Update existing credential management app's policy
852                         removeOrphanedKeyPairs(authenticationPolicy);
853                     } else {
854                         // Replace existing credential management app
855                         removeOrphanedKeyPairs(null);
856                         setManageCredentialsAppOps(existingPackage, false);
857                     }
858                 }
859                 setManageCredentialsAppOps(packageName, true);
860                 mCredentialManagementApp = new CredentialManagementApp(packageName,
861                         authenticationPolicy);
862                 mStateStorage.saveCredentialManagementApp(mCredentialManagementApp);
863             }
864         }
865 
866         private void setManageCredentialsAppOps(String packageName, boolean allowed) {
867             try {
868                 int mode = allowed ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_DEFAULT;
869                 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(packageName, 0);
870                 getSystemService(AppOpsManager.class).setMode(AppOpsManager.OP_MANAGE_CREDENTIALS,
871                         appInfo.uid, packageName, mode);
872             } catch (PackageManager.NameNotFoundException e) {
873                 Log.e(TAG, "Unable to find info for package: " + packageName);
874             }
875         }
876 
877         private void removeOrphanedKeyPairs(
878                 @Nullable AppUriAuthenticationPolicy newPolicy) {
879             Set<String> existingAliases = mCredentialManagementApp.getAuthenticationPolicy()
880                     .getAliases();
881             Set<String> newAliases = newPolicy != null ? newPolicy.getAliases() : new HashSet<>();
882 
883             // Uninstall all certificates that are no longer included in the new
884             // authentication policy
885             for (String existingAlias : existingAliases) {
886                 if (!newAliases.contains(existingAlias)) {
887                     removeKeyPairInternal(existingAlias);
888                 }
889             }
890         }
891 
892         private void checkValidAuthenticationPolicy(
893                 @NonNull AppUriAuthenticationPolicy authenticationPolicy) {
894             if (authenticationPolicy == null
895                     || authenticationPolicy.getAppAndUriMappings().isEmpty()) {
896                 throw new IllegalArgumentException("The authentication policy is null or empty");
897             }
898             // Check whether any of the aliases in the policy already exist
899             for (String alias : authenticationPolicy.getAliases()) {
900                 if (requestPrivateKey(alias) != null) {
901                     throw new IllegalArgumentException(String.format("The authentication policy "
902                             + "contains an installed alias: %s", alias));
903                 }
904             }
905         }
906 
907         @Override
908         public boolean hasCredentialManagementApp() {
909             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
910             synchronized (mCredentialManagementAppLock) {
911                 return mCredentialManagementApp != null;
912             }
913         }
914 
915         @Nullable
916         @Override
917         public String getCredentialManagementAppPackageName() {
918             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
919             synchronized (mCredentialManagementAppLock) {
920                 return mCredentialManagementApp != null
921                         ? mCredentialManagementApp.getPackageName()
922                         : null;
923             }
924         }
925 
926         @Nullable
927         @Override
928         public AppUriAuthenticationPolicy getCredentialManagementAppPolicy() {
929             final CallerIdentity caller = getCaller();
930             Preconditions.checkCallAuthorization(isSystemUid(caller)
931                             || isCredentialManagementApp(caller), MSG_NOT_SYSTEM_OR_CRED_MNG_APP);
932             synchronized (mCredentialManagementAppLock) {
933                 return mCredentialManagementApp != null
934                         ? mCredentialManagementApp.getAuthenticationPolicy()
935                         : null;
936             }
937         }
938 
939         @Nullable
940         @Override
941         public String getPredefinedAliasForPackageAndUri(@NonNull String packageName,
942                 @Nullable Uri uri) {
943             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
944             synchronized (mCredentialManagementAppLock) {
945                 if (mCredentialManagementApp == null || uri == null) {
946                     return null;
947                 }
948                 Map<Uri, String> urisToAliases = mCredentialManagementApp.getAuthenticationPolicy()
949                         .getAppAndUriMappings().get(packageName);
950                 return urisToAliases != null ? urisToAliases.get(uri) : null;
951             }
952         }
953 
954         @Override
955         public void removeCredentialManagementApp() {
956             final CallerIdentity caller = getCaller();
957             Preconditions.checkCallAuthorization(isSystemUid(caller)
958                             || isCredentialManagementApp(caller)
959                             || hasManageCredentialManagementAppPermission(caller),
960                     MSG_NOT_SYSTEM_OR_CRED_MNG_APP);
961             synchronized (mCredentialManagementAppLock) {
962                 if (mCredentialManagementApp != null) {
963                     // Remove all certificates
964                     removeOrphanedKeyPairs(null);
965                     setManageCredentialsAppOps(mCredentialManagementApp.getPackageName(), false);
966                 }
967                 mCredentialManagementApp = null;
968                 mStateStorage.saveCredentialManagementApp(mCredentialManagementApp);
969             }
970         }
971 
972         @Override
973         public boolean isCredentialManagementApp(@NonNull String packageName) {
974             final CallerIdentity caller = getCaller();
975             Preconditions.checkCallAuthorization(isSystemUid(caller)
976                     || isCredentialManagementApp(caller), MSG_NOT_SYSTEM_OR_CRED_MNG_APP);
977             synchronized (mCredentialManagementAppLock) {
978                 return packageName.equals(mCredentialManagementApp.getPackageName());
979             }
980         }
981     };
982 
onBind(Intent intent)983     @Override public IBinder onBind(Intent intent) {
984         if (IKeyChainService.class.getName().equals(intent.getAction())) {
985             return mIKeyChainService;
986         }
987         return null;
988     }
989 
990     @Override
onHandleIntent(final Intent intent)991     protected void onHandleIntent(final Intent intent) {
992         if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
993             mGrantsDb.purgeOldGrants(getPackageManager());
994         }
995     }
996 
broadcastLegacyStorageChange()997     private void broadcastLegacyStorageChange() {
998         Intent intent = new Intent(KeyChain.ACTION_STORAGE_CHANGED);
999         BroadcastOptions opts = BroadcastOptions.makeBasic();
1000         opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.N_MR1);
1001         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()), null, opts.toBundle());
1002     }
1003 
broadcastKeychainChange()1004     private void broadcastKeychainChange() {
1005         Intent intent = new Intent(KeyChain.ACTION_KEYCHAIN_CHANGED);
1006         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
1007     }
1008 
broadcastTrustStoreChange()1009     private void broadcastTrustStoreChange() {
1010         Intent intent = new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED);
1011         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
1012     }
1013 
broadcastPermissionChange(int uid, String alias, boolean access)1014     private void broadcastPermissionChange(int uid, String alias, boolean access) {
1015         // Since the permission change only impacts one uid only send to that uid's packages.
1016         final PackageManager packageManager = getPackageManager();
1017         String[] packages = packageManager.getPackagesForUid(uid);
1018         if (packages == null) {
1019             return;
1020         }
1021         for (String pckg : packages) {
1022             Intent intent = new Intent(KeyChain.ACTION_KEY_ACCESS_CHANGED);
1023             intent.putExtra(KeyChain.EXTRA_KEY_ALIAS, alias);
1024             intent.putExtra(KeyChain.EXTRA_KEY_ACCESSIBLE, access);
1025             intent.setPackage(pckg);
1026             sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
1027         }
1028     }
1029 
emptyOrBase64Encoded(byte[] cert)1030     private static String emptyOrBase64Encoded(byte[] cert) {
1031         if (cert == null) {
1032             return "";
1033         }
1034         return Base64.encodeToString(cert, Base64.NO_WRAP);
1035     }
1036 
1037     private final class CallerIdentity {
1038 
1039         final int mUid;
1040         final int mPid;
1041         final String mPackageName;
1042 
CallerIdentity()1043         CallerIdentity() {
1044             mUid = mInjector.getCallingUid();
1045             mPid = Binder.getCallingPid();
1046             mPackageName = getPackageManager().getNameForUid(mUid);
1047         }
1048     }
1049 
getCaller()1050     private CallerIdentity getCaller() {
1051         return new CallerIdentity();
1052     }
1053 
1054     @VisibleForTesting
setInjector(Injector injector)1055     void setInjector(Injector injector) {
1056         mInjector = injector;
1057     }
1058 
1059     /**
1060      * Injector for mocking out dependencies in tests.
1061      */
1062     @VisibleForTesting
1063     static class Injector {
isSecurityLoggingEnabled()1064         public boolean isSecurityLoggingEnabled() {
1065             return SecurityLog.isLoggingEnabled();
1066         }
1067 
writeSecurityEvent(int tag, Object... payload)1068         public void writeSecurityEvent(int tag, Object... payload) {
1069             SecurityLog.writeEvent(tag, payload);
1070         }
1071 
getCallingUid()1072         public int getCallingUid() {
1073             return Binder.getCallingUid();
1074         }
1075 
getKeyStoreInstance()1076         public KeyStore getKeyStoreInstance() throws KeyStoreException {
1077             return KeyStore.getInstance("AndroidKeyStore");
1078         }
1079     }
1080 }
1081