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 
22 import android.annotation.Nullable;
23 import android.app.BroadcastOptions;
24 import android.app.IntentService;
25 import android.app.admin.SecurityLog;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.pm.PackageManager;
29 import android.content.pm.StringParceledListSlice;
30 import android.os.Binder;
31 import android.os.Build;
32 import android.os.IBinder;
33 import android.os.Process;
34 import android.os.UserHandle;
35 import android.security.Credentials;
36 import android.security.IKeyChainService;
37 import android.security.KeyChain;
38 import android.security.KeyStore;
39 import android.security.keymaster.KeymasterArguments;
40 import android.security.keymaster.KeymasterCertificateChain;
41 import android.security.keystore.AttestationUtils;
42 import android.security.keystore.DeviceIdAttestationException;
43 import android.security.keystore.KeyGenParameterSpec;
44 import android.security.keystore.ParcelableKeyGenParameterSpec;
45 import android.security.keystore.StrongBoxUnavailableException;
46 import android.text.TextUtils;
47 import android.util.Base64;
48 import android.util.Log;
49 
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.internal.widget.LockPatternUtils;
52 import com.android.keychain.internal.ExistingKeysProvider;
53 import com.android.keychain.internal.GrantsDatabase;
54 import com.android.org.conscrypt.TrustedCertificateStore;
55 
56 import java.io.ByteArrayInputStream;
57 import java.io.IOException;
58 import java.security.InvalidAlgorithmParameterException;
59 import java.security.KeyPair;
60 import java.security.KeyPairGenerator;
61 import java.security.NoSuchAlgorithmException;
62 import java.security.NoSuchProviderException;
63 import java.security.cert.Certificate;
64 import java.security.cert.CertificateEncodingException;
65 import java.security.cert.CertificateException;
66 import java.security.cert.CertificateFactory;
67 import java.security.cert.X509Certificate;
68 import java.util.ArrayList;
69 import java.util.Arrays;
70 import java.util.Collections;
71 import java.util.HashSet;
72 import java.util.List;
73 import java.util.Set;
74 
75 import javax.security.auth.x500.X500Principal;
76 
77 public class KeyChainService extends IntentService {
78 
79     private static final String TAG = "KeyChain";
80     private static final String CERT_INSTALLER_PACKAGE = "com.android.certinstaller";
81     private final Set<Integer> ALLOWED_UIDS = Collections.unmodifiableSet(
82             new HashSet(Arrays.asList(KeyStore.UID_SELF, Process.WIFI_UID)));
83 
84     /** created in onCreate(), closed in onDestroy() */
85     private GrantsDatabase mGrantsDb;
86     private Injector mInjector;
87     private final KeyStore mKeyStore = KeyStore.getInstance();
88 
KeyChainService()89     public KeyChainService() {
90         super(KeyChainService.class.getSimpleName());
91         mInjector = new Injector();
92     }
93 
onCreate()94     @Override public void onCreate() {
95         super.onCreate();
96         mGrantsDb = new GrantsDatabase(this, new KeyStoreAliasesProvider(mKeyStore));
97     }
98 
99     @Override
onDestroy()100     public void onDestroy() {
101         super.onDestroy();
102         mGrantsDb.destroy();
103         mGrantsDb = null;
104     }
105 
106     private static class KeyStoreAliasesProvider implements ExistingKeysProvider {
107         private final KeyStore mKeyStore;
108 
KeyStoreAliasesProvider(KeyStore keyStore)109         KeyStoreAliasesProvider(KeyStore keyStore) {
110             mKeyStore = keyStore;
111         }
112 
113         @Override
getExistingKeyAliases()114         public List<String> getExistingKeyAliases() {
115             List<String> aliases = new ArrayList<String>();
116             String[] keyStoreAliases = mKeyStore.list(Credentials.USER_PRIVATE_KEY);
117             if (keyStoreAliases == null) {
118                 return aliases;
119             }
120 
121             for (String alias: keyStoreAliases) {
122                 Log.w(TAG, "Got Alias from KeyStore: " + alias);
123                 String unPrefixedAlias = alias.replaceFirst("^" + Credentials.USER_PRIVATE_KEY, "");
124                 if (!unPrefixedAlias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) {
125                     aliases.add(unPrefixedAlias);
126                 }
127             }
128             return aliases;
129         }
130     }
131 
132     private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() {
133         private final TrustedCertificateStore mTrustedCertificateStore
134                 = new TrustedCertificateStore();
135         private final Context mContext = KeyChainService.this;
136 
137         @Override
138         public String requestPrivateKey(String alias) {
139             if (!hasGrant(alias)) {
140                 return null;
141             }
142 
143             final String keystoreAlias = Credentials.USER_PRIVATE_KEY + alias;
144             final int uid = mInjector.getCallingUid();
145             Log.i(TAG, String.format("UID %d will be granted access to %s", uid, keystoreAlias));
146             return mKeyStore.grant(keystoreAlias, uid);
147         }
148 
149         @Override public byte[] getCertificate(String alias) {
150             if (!hasGrant(alias)) {
151                 return null;
152             }
153             return mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
154         }
155 
156         @Override public byte[] getCaCertificates(String alias) {
157             if (!hasGrant(alias)) {
158                 return null;
159             }
160             return mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
161         }
162 
163         @Override public boolean isUserSelectable(String alias) {
164             validateAlias(alias);
165             return mGrantsDb.isUserSelectable(alias);
166         }
167 
168         @Override public void setUserSelectable(String alias, boolean isUserSelectable) {
169             validateAlias(alias);
170             checkSystemCaller();
171             Log.i(TAG, String.format("Marking certificate %s as user-selectable: %b", alias,
172                     isUserSelectable));
173             mGrantsDb.setIsUserSelectable(alias, isUserSelectable);
174         }
175 
176         @Override public int generateKeyPair(
177                 String algorithm, ParcelableKeyGenParameterSpec parcelableSpec) {
178             checkSystemCaller();
179             final KeyGenParameterSpec spec = parcelableSpec.getSpec();
180             final String alias = spec.getKeystoreAlias();
181 
182             Log.i(TAG, String.format("About to generate key with alias %s, algorithm %s",
183                     alias, algorithm));
184 
185             if (KeyChain.KEY_ALIAS_SELECTION_DENIED.equals(alias)) {
186                 throw new IllegalArgumentException("The alias specified for the key denotes "
187                         + "a reserved value and cannot be used to name a key");
188             }
189             // Validate the alias here to avoid relying on KeyGenParameterSpec c'tor preventing
190             // the creation of a KeyGenParameterSpec instance with a non-empty alias.
191             if (TextUtils.isEmpty(alias) || spec.getUid() != KeyStore.UID_SELF) {
192                 Log.e(TAG, "Cannot generate key pair with empty alias or specified uid.");
193                 return KeyChain.KEY_GEN_MISSING_ALIAS;
194             }
195 
196             if (spec.getAttestationChallenge() != null) {
197                 Log.e(TAG, "Key generation request should not include an Attestation challenge.");
198                 return KeyChain.KEY_GEN_SUPERFLUOUS_ATTESTATION_CHALLENGE;
199             }
200 
201             if (!removeKeyPair(alias)) {
202                 Log.e(TAG, "Failed to remove previously-installed alias " + alias);
203                 //TODO: Introduce a different error code in R to distinguish the failure to remove
204                 // old keys from other failures.
205                 return KeyChain.KEY_GEN_FAILURE;
206             }
207 
208             try {
209                 KeyPairGenerator generator = KeyPairGenerator.getInstance(
210                         algorithm, "AndroidKeyStore");
211                 // Do not prepend USER_PRIVATE_KEY to the alias because
212                 // AndroidKeyStoreKeyPairGeneratorSpi will helpfully prepend that in
213                 // generateKeyPair.
214                 generator.initialize(spec);
215                 KeyPair kp = generator.generateKeyPair();
216                 if (kp == null) {
217                     Log.e(TAG, "Key generation failed.");
218                     return KeyChain.KEY_GEN_FAILURE;
219                 }
220                 return KeyChain.KEY_GEN_SUCCESS;
221             } catch (NoSuchAlgorithmException e) {
222                 Log.e(TAG, "Invalid algorithm requested", e);
223                 return KeyChain.KEY_GEN_NO_SUCH_ALGORITHM;
224             } catch (InvalidAlgorithmParameterException e) {
225                 Log.e(TAG, "Invalid algorithm params", e);
226                 return KeyChain.KEY_GEN_INVALID_ALGORITHM_PARAMETERS;
227             } catch (NoSuchProviderException e) {
228                 Log.e(TAG, "Could not find Keystore.", e);
229                 return KeyChain.KEY_GEN_NO_KEYSTORE_PROVIDER;
230             } catch (StrongBoxUnavailableException e) {
231                 Log.e(TAG, "StrongBox unavailable.", e);
232                 return KeyChain.KEY_GEN_STRONGBOX_UNAVAILABLE;
233             }
234         }
235 
236         @Override public int attestKey(
237                 String alias, byte[] attestationChallenge,
238                 int[] idAttestationFlags,
239                 KeymasterCertificateChain attestationChain) {
240             checkSystemCaller();
241             validateAlias(alias);
242 
243             if (attestationChallenge == null) {
244                 Log.e(TAG, String.format("Missing attestation challenge for alias %s", alias));
245                 return KeyChain.KEY_ATTESTATION_MISSING_CHALLENGE;
246             }
247 
248             if (Log.isLoggable(TAG, Log.DEBUG)) {
249                 Log.d(TAG, String.format("About to attest key alias %s, challenge %s, flags %s",
250                         alias, Base64.encodeToString(attestationChallenge, Base64.NO_WRAP),
251                         Arrays.toString(idAttestationFlags)));
252             }
253 
254             final KeymasterArguments attestArgs;
255             try {
256                 attestArgs = AttestationUtils.prepareAttestationArguments(
257                         mContext, idAttestationFlags, attestationChallenge);
258             } catch (DeviceIdAttestationException e) {
259                 Log.e(TAG, "Failed collecting attestation data", e);
260                 return KeyChain.KEY_ATTESTATION_CANNOT_COLLECT_DATA;
261             }
262             final String keystoreAlias = Credentials.USER_PRIVATE_KEY + alias;
263             final int errorCode = mKeyStore.attestKey(keystoreAlias, attestArgs, attestationChain);
264             if (errorCode != KeyStore.NO_ERROR) {
265                 Log.e(TAG, String.format("Failure attesting for key %s: %d", alias, errorCode));
266                 if (errorCode == KeyStore.CANNOT_ATTEST_IDS) {
267                     return KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS;
268                 } else {
269                     // General failure, cannot discern which.
270                     return KeyChain.KEY_ATTESTATION_FAILURE;
271                 }
272             }
273 
274             return KeyChain.KEY_ATTESTATION_SUCCESS;
275         }
276 
277         @Override public boolean setKeyPairCertificate(String alias, byte[] userCertificate,
278                 byte[] userCertificateChain) {
279             checkSystemCaller();
280             if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertificate,
281                         KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
282                 Log.e(TAG, "Failed to import user certificate " + userCertificate);
283                 return false;
284             }
285 
286             if (userCertificateChain != null && userCertificateChain.length > 0) {
287                 if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, userCertificateChain,
288                             KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
289                     Log.e(TAG, "Failed to import certificate chain" + userCertificateChain);
290                     if (!mKeyStore.delete(Credentials.USER_CERTIFICATE + alias)) {
291                         Log.e(TAG, "Failed to clean up key chain after certificate chain"
292                                 + " importing failed");
293                     }
294                     return false;
295                 }
296             } else {
297                 if (!mKeyStore.delete(Credentials.CA_CERTIFICATE + alias)) {
298                     Log.e(TAG, "Failed to remove CA certificate chain for alias " + alias);
299                 }
300             }
301 
302             if (Log.isLoggable(TAG, Log.DEBUG)) {
303                 Log.d(TAG, String.format("Set certificate for key alias %s : user %s CA chain: %s",
304                         alias, emptyOrBase64Encoded(userCertificate),
305                         emptyOrBase64Encoded(userCertificateChain)));
306             }
307             broadcastKeychainChange();
308             broadcastLegacyStorageChange();
309             return true;
310         }
311 
312         private void validateAlias(String alias) {
313             if (alias == null) {
314                 throw new NullPointerException("alias == null");
315             }
316         }
317 
318         private boolean hasGrant(String alias) {
319             validateAlias(alias);
320 
321             final int callingUid = mInjector.getCallingUid();
322             if (!mGrantsDb.hasGrant(callingUid, alias)) {
323                 Log.w(TAG, String.format(
324                         "uid %d doesn't have permission to access the requested alias %s",
325                         callingUid, alias));
326                 return false;
327             }
328 
329             return true;
330         }
331 
332         @Override public String installCaCertificate(byte[] caCertificate) {
333             checkCertInstallerOrSystemCaller();
334             final String alias;
335             String subjectForAudit = null;
336             try {
337                 final X509Certificate cert = parseCertificate(caCertificate);
338                 final boolean isSecurityLoggingEnabled = mInjector.isSecurityLoggingEnabled();
339                 final boolean isDebugLoggable = Log.isLoggable(TAG, Log.DEBUG);
340                 if (isSecurityLoggingEnabled || isDebugLoggable) {
341                     final String subject =
342                             cert.getSubjectX500Principal().getName(X500Principal.CANONICAL);
343                     if (isDebugLoggable) {
344                         Log.d(TAG, String.format("Installing CA certificate: %s", subject));
345                     }
346                     if (isSecurityLoggingEnabled) {
347                         subjectForAudit = subject;
348                     }
349                 }
350                 synchronized (mTrustedCertificateStore) {
351                     mTrustedCertificateStore.installCertificate(cert);
352                     alias = mTrustedCertificateStore.getCertificateAlias(cert);
353                 }
354             } catch (IOException | CertificateException e) {
355                 Log.w(TAG, "Failed installing CA certificate", e);
356                 if (subjectForAudit != null) {
357                     mInjector.writeSecurityEvent(
358                             TAG_CERT_AUTHORITY_INSTALLED, 0 /*result*/, subjectForAudit,
359                             UserHandle.myUserId());
360                 }
361                 throw new IllegalStateException(e);
362             }
363             if (subjectForAudit != null) {
364                 mInjector.writeSecurityEvent(
365                         TAG_CERT_AUTHORITY_INSTALLED, 1 /*result*/, subjectForAudit,
366                         UserHandle.myUserId());
367             }
368             broadcastLegacyStorageChange();
369             broadcastTrustStoreChange();
370             return alias;
371         }
372 
373         /**
374          * Install a key pair to the keystore.
375          *
376          * @param privateKey The private key associated with the client certificate
377          * @param userCertificate The client certificate to be installed
378          * @param userCertificateChain The rest of the chain for the client certificate
379          * @param alias The alias under which the key pair is installed. It is invalid to pass
380          *              {@code KeyChain.KEY_ALIAS_SELECTION_DENIED}.
381          * @param uid Can be only one of two values: Either {@code KeyStore.UID_SELF} to indicate
382          *            installation into the current user's system Keystore instance, or
383          *            {@code Process.WIFI_UID} to indicate installation into the main user's
384          *            WiFi Keystore instance. It is only valid to pass {@code Process.WIFI_UID} to
385          *            the KeyChain service on user 0.
386          * @return Whether the operation succeeded or not.
387          */
388         @Override public boolean installKeyPair(@Nullable byte[] privateKey,
389                 @Nullable byte[] userCertificate, @Nullable byte[] userCertificateChain,
390                 String alias, int uid) {
391             checkCertInstallerOrSystemCaller();
392             if (KeyChain.KEY_ALIAS_SELECTION_DENIED.equals(alias)) {
393                 throw new IllegalArgumentException("The alias specified for the key denotes "
394                         + "a reserved value and cannot be used to name a key");
395             }
396             if (!ALLOWED_UIDS.contains(uid)) {
397                 Log.e(TAG,
398                         String.format("Installing alias %s as UID %d is now allowed.", alias, uid));
399                 return false;
400             }
401 
402             if (privateKey == null && userCertificate == null && userCertificateChain == null) {
403                 Log.e(TAG, String.format("Nothing to install for alias %s", alias));
404                 return false;
405             }
406 
407             if (uid == Process.WIFI_UID && UserHandle.myUserId() != UserHandle.USER_SYSTEM) {
408                 Log.e(TAG, String.format(
409                         "Installation into the WiFi Keystore should be called from the primary "
410                                 + "user, not user %d",
411                         UserHandle.myUserId()));
412                 return false;
413             }
414 
415             if (Log.isLoggable(TAG, Log.DEBUG)) {
416                 Log.d(TAG, String.format("Installing certificate and key to alias %s to uid %d: "
417                                 + "user cert %s CA chain: %s", alias, uid,
418                                 emptyOrBase64Encoded(userCertificate),
419                                 emptyOrBase64Encoded(userCertificateChain)));
420             }
421 
422             if (!removeKeyPair(alias)) {
423                 return false;
424             }
425             if (privateKey != null && !mKeyStore.importKey(
426                     Credentials.USER_PRIVATE_KEY + alias, privateKey, uid, KeyStore.FLAG_NONE)) {
427                 Log.e(TAG, "Failed to import private key " + alias);
428                 return false;
429             }
430             if (userCertificate != null &&
431                     !mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertificate,
432                         uid, KeyStore.FLAG_NONE)) {
433                 Log.e(TAG, "Failed to import user certificate " + userCertificate);
434                 if (!mKeyStore.delete(Credentials.USER_PRIVATE_KEY + alias)) {
435                     Log.e(TAG, "Failed to delete private key after certificate importing failed");
436                 }
437                 return false;
438             }
439             if (userCertificateChain != null && userCertificateChain.length > 0) {
440                 if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, userCertificateChain, uid,
441                         KeyStore.FLAG_NONE)) {
442                     Log.e(TAG, "Failed to import certificate chain" + userCertificateChain);
443                     if (!removeKeyPair(alias)) {
444                         Log.e(TAG, "Failed to clean up key chain after certificate chain"
445                                 + " importing failed");
446                     }
447                     return false;
448                 }
449             }
450             broadcastKeychainChange();
451             broadcastLegacyStorageChange();
452             return true;
453         }
454 
455         @Override public boolean removeKeyPair(String alias) {
456             checkCertInstallerOrSystemCaller();
457             if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
458                 return false;
459             }
460             Log.w(TAG, String.format(
461                     "WARNING: Removing alias %s, existing grants will be revoked.", alias));
462             mGrantsDb.removeAliasInformation(alias);
463             broadcastKeychainChange();
464             broadcastLegacyStorageChange();
465             return true;
466         }
467 
468         private X509Certificate parseCertificate(byte[] bytes) throws CertificateException {
469             CertificateFactory cf = CertificateFactory.getInstance("X.509");
470             return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(bytes));
471         }
472 
473         @Override public boolean reset() {
474             // only Settings should be able to reset
475             checkSystemCaller();
476             mGrantsDb.removeAllAliasesInformation();
477             boolean ok = true;
478             synchronized (mTrustedCertificateStore) {
479                 // delete user-installed CA certs
480                 for (String alias : mTrustedCertificateStore.aliases()) {
481                     if (TrustedCertificateStore.isUser(alias)) {
482                         if (!deleteCertificateEntry(alias)) {
483                             ok = false;
484                         }
485                     }
486                 }
487             }
488             broadcastTrustStoreChange();
489             broadcastKeychainChange();
490             broadcastLegacyStorageChange();
491             return ok;
492         }
493 
494         @Override public boolean deleteCaCertificate(String alias) {
495             // only Settings should be able to delete
496             checkSystemCaller();
497             boolean ok = true;
498             Log.i(TAG, String.format("Deleting CA certificate %s", alias));
499             synchronized (mTrustedCertificateStore) {
500                 ok = deleteCertificateEntry(alias);
501             }
502             broadcastTrustStoreChange();
503             broadcastLegacyStorageChange();
504             return ok;
505         }
506 
507         private boolean deleteCertificateEntry(String alias) {
508             String subjectForAudit = null;
509             if (mInjector.isSecurityLoggingEnabled()) {
510                 final Certificate cert = mTrustedCertificateStore.getCertificate(alias);
511                 if (cert instanceof X509Certificate) {
512                     subjectForAudit = ((X509Certificate) cert)
513                             .getSubjectX500Principal().getName(X500Principal.CANONICAL);
514                 }
515             }
516 
517             try {
518                 mTrustedCertificateStore.deleteCertificateEntry(alias);
519                 if (subjectForAudit != null) {
520                     mInjector.writeSecurityEvent(
521                             TAG_CERT_AUTHORITY_REMOVED, 1 /*result*/, subjectForAudit,
522                             UserHandle.myUserId());
523                 }
524                 return true;
525             } catch (IOException | CertificateException e) {
526                 Log.w(TAG, "Problem removing CA certificate " + alias, e);
527                 if (subjectForAudit != null) {
528                     mInjector.writeSecurityEvent(
529                             TAG_CERT_AUTHORITY_REMOVED, 0 /*result*/, subjectForAudit,
530                             UserHandle.myUserId());
531                 }
532                 return false;
533             }
534         }
535 
536         private void checkCertInstallerOrSystemCaller() {
537             final String caller = callingPackage();
538             if (!isCallerWithSystemUid() && !CERT_INSTALLER_PACKAGE.equals(caller)) {
539                 throw new SecurityException("Not system or cert installer package: " + caller);
540             }
541         }
542 
543         private void checkSystemCaller() {
544             if (!isCallerWithSystemUid()) {
545                 throw new SecurityException("Not system package: " + callingPackage());
546             }
547         }
548 
549         private boolean isCallerWithSystemUid() {
550             return UserHandle.isSameApp(mInjector.getCallingUid(), Process.SYSTEM_UID);
551         }
552 
553         private String callingPackage() {
554             return getPackageManager().getNameForUid(mInjector.getCallingUid());
555         }
556 
557         @Override public boolean hasGrant(int uid, String alias) {
558             checkSystemCaller();
559             return mGrantsDb.hasGrant(uid, alias);
560         }
561 
562         @Override public void setGrant(int uid, String alias, boolean value) {
563             checkSystemCaller();
564             mGrantsDb.setGrant(uid, alias, value);
565             broadcastPermissionChange(uid, alias, value);
566             broadcastLegacyStorageChange();
567         }
568 
569         @Override
570         public StringParceledListSlice getUserCaAliases() {
571             synchronized (mTrustedCertificateStore) {
572                 return new StringParceledListSlice(new ArrayList<String>(
573                         mTrustedCertificateStore.userAliases()));
574             }
575         }
576 
577         @Override
578         public StringParceledListSlice getSystemCaAliases() {
579             synchronized (mTrustedCertificateStore) {
580                 return new StringParceledListSlice(new ArrayList<String>(
581                         mTrustedCertificateStore.allSystemAliases()));
582             }
583         }
584 
585         @Override
586         public boolean containsCaAlias(String alias) {
587             return mTrustedCertificateStore.containsAlias(alias);
588         }
589 
590         @Override
591         public byte[] getEncodedCaCertificate(String alias, boolean includeDeletedSystem) {
592             synchronized (mTrustedCertificateStore) {
593                 X509Certificate certificate = (X509Certificate) mTrustedCertificateStore
594                         .getCertificate(alias, includeDeletedSystem);
595                 if (certificate == null) {
596                     Log.w(TAG, "Could not find CA certificate " + alias);
597                     return null;
598                 }
599                 try {
600                     return certificate.getEncoded();
601                 } catch (CertificateEncodingException e) {
602                     Log.w(TAG, "Error while encoding CA certificate " + alias);
603                     return null;
604                 }
605             }
606         }
607 
608         @Override
609         public List<String> getCaCertificateChainAliases(String rootAlias,
610                 boolean includeDeletedSystem) {
611             synchronized (mTrustedCertificateStore) {
612                 X509Certificate root = (X509Certificate) mTrustedCertificateStore.getCertificate(
613                         rootAlias, includeDeletedSystem);
614                 try {
615                     List<X509Certificate> chain = mTrustedCertificateStore.getCertificateChain(
616                             root);
617                     List<String> aliases = new ArrayList<String>(chain.size());
618                     final int n = chain.size();
619                     for (int i = 0; i < n; ++i) {
620                         String alias = mTrustedCertificateStore.getCertificateAlias(chain.get(i),
621                                 true);
622                         if (alias != null) {
623                             aliases.add(alias);
624                         }
625                     }
626                     return aliases;
627                 } catch (CertificateException e) {
628                     Log.w(TAG, "Error retrieving cert chain for root " + rootAlias);
629                     return Collections.emptyList();
630                 }
631             }
632         }
633     };
634 
onBind(Intent intent)635     @Override public IBinder onBind(Intent intent) {
636         if (IKeyChainService.class.getName().equals(intent.getAction())) {
637             return mIKeyChainService;
638         }
639         return null;
640     }
641 
642     @Override
onHandleIntent(final Intent intent)643     protected void onHandleIntent(final Intent intent) {
644         if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
645             mGrantsDb.purgeOldGrants(getPackageManager());
646         }
647     }
648 
broadcastLegacyStorageChange()649     private void broadcastLegacyStorageChange() {
650         Intent intent = new Intent(KeyChain.ACTION_STORAGE_CHANGED);
651         BroadcastOptions opts = BroadcastOptions.makeBasic();
652         opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.N_MR1);
653         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()), null, opts.toBundle());
654     }
655 
broadcastKeychainChange()656     private void broadcastKeychainChange() {
657         Intent intent = new Intent(KeyChain.ACTION_KEYCHAIN_CHANGED);
658         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
659     }
660 
broadcastTrustStoreChange()661     private void broadcastTrustStoreChange() {
662         Intent intent = new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED);
663         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
664     }
665 
broadcastPermissionChange(int uid, String alias, boolean access)666     private void broadcastPermissionChange(int uid, String alias, boolean access) {
667         // Since the permission change only impacts one uid only send to that uid's packages.
668         final PackageManager packageManager = getPackageManager();
669         String[] packages = packageManager.getPackagesForUid(uid);
670         if (packages == null) {
671             return;
672         }
673         for (String pckg : packages) {
674             Intent intent = new Intent(KeyChain.ACTION_KEY_ACCESS_CHANGED);
675             intent.putExtra(KeyChain.EXTRA_KEY_ALIAS, alias);
676             intent.putExtra(KeyChain.EXTRA_KEY_ACCESSIBLE, access);
677             intent.setPackage(pckg);
678             sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
679         }
680     }
681 
emptyOrBase64Encoded(byte[] cert)682     private static String emptyOrBase64Encoded(byte[] cert) {
683         if (cert == null) {
684             return "";
685         }
686         return Base64.encodeToString(cert, Base64.NO_WRAP);
687     }
688 
689     @VisibleForTesting
setInjector(Injector injector)690     void setInjector(Injector injector) {
691         mInjector = injector;
692     }
693 
694     /**
695      * Injector for mocking out dependencies in tests.
696      */
697     @VisibleForTesting
698     static class Injector {
isSecurityLoggingEnabled()699         public boolean isSecurityLoggingEnabled() {
700             return SecurityLog.isLoggingEnabled();
701         }
702 
writeSecurityEvent(int tag, Object... payload)703         public void writeSecurityEvent(int tag, Object... payload) {
704             SecurityLog.writeEvent(tag, payload);
705         }
706 
getCallingUid()707         public int getCallingUid() {
708             return Binder.getCallingUid();
709         }
710     }
711 }
712