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.Process; 41 import android.os.UserHandle; 42 import android.security.AppUriAuthenticationPolicy; 43 import android.security.CredentialManagementApp; 44 import android.security.IKeyChainService; 45 import android.security.KeyChain; 46 import android.security.KeyStore2; 47 import android.security.keystore.KeyGenParameterSpec; 48 import android.security.keystore.KeyProperties; 49 import android.security.keystore.ParcelableKeyGenParameterSpec; 50 import android.security.keystore.StrongBoxUnavailableException; 51 import android.security.keystore2.AndroidKeyStoreLoadStoreParameter; 52 import android.system.keystore2.Domain; 53 import android.system.keystore2.KeyDescriptor; 54 import android.system.keystore2.KeyPermission; 55 import android.text.TextUtils; 56 import android.util.Base64; 57 import android.util.Log; 58 59 import com.android.internal.annotations.GuardedBy; 60 import com.android.internal.annotations.VisibleForTesting; 61 import com.android.internal.util.Preconditions; 62 import com.android.keychain.internal.ExistingKeysProvider; 63 import com.android.keychain.internal.GrantsDatabase; 64 import com.android.org.conscrypt.TrustedCertificateStore; 65 66 import java.io.ByteArrayInputStream; 67 import java.io.ByteArrayOutputStream; 68 import java.io.IOException; 69 import java.security.InvalidAlgorithmParameterException; 70 import java.security.Key; 71 import java.security.KeyFactory; 72 import java.security.KeyPair; 73 import java.security.KeyPairGenerator; 74 import java.security.KeyStore; 75 import java.security.KeyStoreException; 76 import java.security.NoSuchAlgorithmException; 77 import java.security.NoSuchProviderException; 78 import java.security.PrivateKey; 79 import java.security.ProviderException; 80 import java.security.UnrecoverableKeyException; 81 import java.security.cert.Certificate; 82 import java.security.cert.CertificateEncodingException; 83 import java.security.cert.CertificateException; 84 import java.security.cert.CertificateFactory; 85 import java.security.cert.X509Certificate; 86 import java.security.spec.InvalidKeySpecException; 87 import java.security.spec.PKCS8EncodedKeySpec; 88 import java.util.ArrayList; 89 import java.util.Arrays; 90 import java.util.Collection; 91 import java.util.Collections; 92 import java.util.Enumeration; 93 import java.util.HashSet; 94 import java.util.List; 95 import java.util.Map; 96 import java.util.Set; 97 98 import javax.security.auth.x500.X500Principal; 99 100 public class KeyChainService extends IntentService { 101 102 private static final String TAG = "KeyChain"; 103 private static final String CERT_INSTALLER_PACKAGE = "com.android.certinstaller"; 104 private final Set<Integer> ALLOWED_UIDS = Collections.unmodifiableSet( 105 new HashSet(Arrays.asList(UID_SELF, Process.WIFI_UID))); 106 107 private static final String MSG_NOT_SYSTEM = "Not system package"; 108 private static final String MSG_NOT_SYSTEM_OR_CERT_INSTALLER = 109 "Not system or cert installer package"; 110 private static final String MSG_NOT_SYSTEM_OR_CRED_MNG_APP = 111 "Not system or credential management app package"; 112 113 /** created in onCreate(), closed in onDestroy() */ 114 private GrantsDatabase mGrantsDb; 115 private Injector mInjector; 116 private KeyStore mKeyStore; 117 private KeyChainStateStorage mStateStorage; 118 119 private Object mCredentialManagementAppLock = new Object(); 120 @Nullable 121 @GuardedBy("mCredentialManagementAppLock") 122 private CredentialManagementApp mCredentialManagementApp; 123 KeyChainService()124 public KeyChainService() { 125 super(KeyChainService.class.getSimpleName()); 126 mInjector = new Injector(); 127 } 128 getKeyStore()129 private KeyStore getKeyStore() { 130 try { 131 final KeyStore keystore = mInjector.getKeyStoreInstance(); 132 keystore.load(null); 133 return keystore; 134 } catch (KeyStoreException | IOException | NoSuchAlgorithmException 135 | CertificateException e) { 136 Log.e(TAG, "Error opening AndroidKeyStore.", e); 137 throw new RuntimeException("Error opening AndroidKeyStore.", e); 138 } 139 } 140 getKeyStore(boolean useWifiNamespace)141 private KeyStore getKeyStore(boolean useWifiNamespace) { 142 if (!useWifiNamespace) { 143 return mKeyStore; 144 } 145 try { 146 final KeyStore keystore = mInjector.getKeyStoreInstance(); 147 keystore.load( 148 new AndroidKeyStoreLoadStoreParameter( 149 KeyProperties.NAMESPACE_WIFI)); 150 return keystore; 151 } catch (IOException | CertificateException | KeyStoreException 152 | NoSuchAlgorithmException e) { 153 Log.e(TAG, "Failed to open AndroidKeyStore for WI-FI namespace.", e); 154 return null; 155 } 156 } 157 onCreate()158 @Override public void onCreate() { 159 super.onCreate(); 160 mKeyStore = getKeyStore(); 161 mGrantsDb = new GrantsDatabase(this, new KeyStoreAliasesProvider(mKeyStore)); 162 mStateStorage = new KeyChainStateStorage(getDataDir()); 163 164 synchronized (mCredentialManagementAppLock) { 165 mCredentialManagementApp = mStateStorage.loadCredentialManagementApp(); 166 } 167 } 168 169 @Override onDestroy()170 public void onDestroy() { 171 super.onDestroy(); 172 mGrantsDb.destroy(); 173 mGrantsDb = null; 174 } 175 176 private static class KeyStoreAliasesProvider implements ExistingKeysProvider { 177 private final KeyStore mKeyStore; 178 KeyStoreAliasesProvider(KeyStore keyStore)179 KeyStoreAliasesProvider(KeyStore keyStore) { 180 mKeyStore = keyStore; 181 } 182 183 @Override getExistingKeyAliases()184 public List<String> getExistingKeyAliases() { 185 final List<String> keyStoreAliases = new ArrayList<>(); 186 try { 187 final Enumeration<String> aliases = mKeyStore.aliases(); 188 while (aliases.hasMoreElements()) { 189 final String alias = aliases.nextElement(); 190 if (mKeyStore.isKeyEntry(alias)) { 191 keyStoreAliases.add(alias); 192 } 193 } 194 } catch (KeyStoreException e) { 195 Log.e(TAG, "Error while loading entries from keystore. " 196 + "List may be empty or incomplete."); 197 } 198 199 return keyStoreAliases; 200 } 201 } 202 makeKeyDescriptor(String alias)203 private KeyDescriptor makeKeyDescriptor(String alias) { 204 final KeyDescriptor key = new KeyDescriptor(); 205 key.domain = Domain.APP; 206 key.nspace = KeyProperties.NAMESPACE_APPLICATION; 207 key.alias = alias; 208 key.blob = null; 209 return key; 210 } 211 212 private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() { 213 private final TrustedCertificateStore mTrustedCertificateStore 214 = new TrustedCertificateStore(); 215 private final Context mContext = KeyChainService.this; 216 217 @Override 218 public String requestPrivateKey(String alias) { 219 final CallerIdentity caller = getCaller(); 220 if (!hasGrant(alias, caller)) { 221 return null; 222 } 223 224 final int granteeUid = caller.mUid; 225 226 try { 227 final KeyStore2 keyStore2 = KeyStore2.getInstance(); 228 KeyDescriptor grant = keyStore2.grant(makeKeyDescriptor(alias), granteeUid, 229 KeyPermission.USE | KeyPermission.GET_INFO); 230 return KeyChain.getGrantString(grant); 231 } catch (android.security.KeyStoreException e) { 232 Log.e(TAG, "Failed to grant " + alias + " to uid: " + granteeUid, e); 233 return null; 234 } 235 } 236 237 @Override 238 public String getWifiKeyGrantAsUser(String alias) { 239 Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM); 240 241 if (!hasGrant(alias, Process.WIFI_UID)) { 242 return null; 243 } 244 245 KeyStore2 keyStore2 = KeyStore2.getInstance(); 246 try { 247 KeyDescriptor grant = keyStore2.grant(makeKeyDescriptor(alias), 248 Process.WIFI_UID, KeyPermission.USE | KeyPermission.GET_INFO); 249 return KeyStore2.makeKeystoreEngineGrantString(grant.nspace); 250 } catch (android.security.KeyStoreException e) { 251 Log.e(TAG, "Failed to grant " + alias + " to uid: " + Process.WIFI_UID, e); 252 return null; 253 } 254 } 255 256 @Override public byte[] getCertificate(String alias) { 257 final CallerIdentity caller = getCaller(); 258 if (!hasGrant(alias, caller) && !isSystemUid(caller)) { 259 return null; 260 } 261 try { 262 if (!mKeyStore.isCertificateEntry(alias)) { 263 final Certificate cert = mKeyStore.getCertificate(alias); 264 if (cert == null) return null; 265 return cert.getEncoded(); 266 } else { 267 return null; 268 } 269 } catch (KeyStoreException | CertificateEncodingException e) { 270 Log.e(TAG, "Failed to retrieve certificate.", e); 271 return null; 272 } 273 } 274 275 @Override public byte[] getCaCertificates(String alias) { 276 final CallerIdentity caller = getCaller(); 277 if (!hasGrant(alias, caller) && !isSystemUid(caller)) { 278 return null; 279 } 280 try { 281 if (mKeyStore.isCertificateEntry(alias)) { 282 return mKeyStore.getCertificate(alias).getEncoded(); 283 } else { 284 final Certificate[] certs = mKeyStore.getCertificateChain(alias); 285 if (certs == null || certs.length <= 1) return null; 286 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 287 for (int i = 1; i < certs.length; ++i) { 288 outputStream.write(certs[i].getEncoded()); 289 } 290 return outputStream.toByteArray(); 291 } 292 } catch (KeyStoreException | CertificateEncodingException | IOException e) { 293 Log.e(TAG, "Failed to retrieve certificate(s) from AndroidKeyStore.", e); 294 return null; 295 } 296 } 297 298 @Override public boolean isUserSelectable(String alias) { 299 validateAlias(alias); 300 return mGrantsDb.isUserSelectable(alias); 301 } 302 303 @Override public void setUserSelectable(String alias, boolean isUserSelectable) { 304 validateAlias(alias); 305 Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM); 306 Log.i(TAG, String.format("Marking certificate %s as user-selectable: %b", alias, 307 isUserSelectable)); 308 mGrantsDb.setIsUserSelectable(alias, isUserSelectable); 309 } 310 311 @Override public int generateKeyPair( 312 String algorithm, ParcelableKeyGenParameterSpec parcelableSpec) { 313 Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM); 314 final KeyGenParameterSpec spec = parcelableSpec.getSpec(); 315 final String alias = spec.getKeystoreAlias(); 316 317 Log.i(TAG, String.format("About to generate key with alias %s, algorithm %s", 318 alias, algorithm)); 319 320 if (KeyChain.KEY_ALIAS_SELECTION_DENIED.equals(alias)) { 321 throw new IllegalArgumentException("The alias specified for the key denotes " 322 + "a reserved value and cannot be used to name a key"); 323 } 324 // Validate the alias here to avoid relying on KeyGenParameterSpec c'tor preventing 325 // the creation of a KeyGenParameterSpec instance with a non-empty alias. 326 if (TextUtils.isEmpty(alias) || spec.getUid() != UID_SELF) { 327 Log.e(TAG, "Cannot generate key pair with empty alias or specified uid."); 328 return KeyChain.KEY_GEN_MISSING_ALIAS; 329 } 330 331 try { 332 KeyPairGenerator generator = KeyPairGenerator.getInstance( 333 algorithm, "AndroidKeyStore"); 334 // Do not prepend USER_PRIVATE_KEY to the alias because 335 // AndroidKeyStoreKeyPairGeneratorSpi will helpfully prepend that in 336 // generateKeyPair. 337 generator.initialize(spec); 338 KeyPair kp = generator.generateKeyPair(); 339 if (kp == null) { 340 Log.e(TAG, "Key generation failed."); 341 return KeyChain.KEY_GEN_FAILURE; 342 } 343 return KeyChain.KEY_GEN_SUCCESS; 344 } catch (NoSuchAlgorithmException e) { 345 Log.e(TAG, "Invalid algorithm requested", e); 346 return KeyChain.KEY_GEN_NO_SUCH_ALGORITHM; 347 } catch (InvalidAlgorithmParameterException e) { 348 Log.e(TAG, "Invalid algorithm params", e); 349 return KeyChain.KEY_GEN_INVALID_ALGORITHM_PARAMETERS; 350 } catch (NoSuchProviderException e) { 351 Log.e(TAG, "Could not find Keystore.", e); 352 return KeyChain.KEY_GEN_NO_KEYSTORE_PROVIDER; 353 } catch (StrongBoxUnavailableException e) { 354 Log.e(TAG, "StrongBox unavailable.", e); 355 return KeyChain.KEY_GEN_STRONGBOX_UNAVAILABLE; 356 } catch (ProviderException e) { 357 Throwable t = e.getCause(); 358 if (t instanceof android.security.KeyStoreException) { 359 if (((android.security.KeyStoreException) t).getErrorCode() 360 == ErrorCode.CANNOT_ATTEST_IDS) { 361 return KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS; 362 } 363 } 364 Log.e(TAG, "KeyStore error.", e); 365 return KeyChain.KEY_GEN_FAILURE; 366 } 367 } 368 369 @Override public boolean setKeyPairCertificate(String alias, byte[] userCertificate, 370 byte[] userCertificateChain) { 371 Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM); 372 373 final PrivateKey privateKey; 374 try { 375 final Key key = mKeyStore.getKey(alias, null); 376 if (! (key instanceof PrivateKey)) { 377 return false; 378 } 379 privateKey = (PrivateKey) key; 380 } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) { 381 Log.e(TAG, "Failed to get private key entry.", e); 382 return false; 383 } 384 385 final ArrayList<Certificate> certs = new ArrayList<>(); 386 try { 387 if (userCertificate != null) { 388 certs.add(parseCertificate(userCertificate)); 389 } 390 if (userCertificateChain != null) { 391 certs.addAll(parseCertificates(userCertificateChain)); 392 } 393 } catch (CertificateException e) { 394 Log.e(TAG, "Failed to parse user certificate.", e); 395 return false; 396 } 397 398 final Certificate[] certsArray = certs.toArray(new Certificate[0]); 399 400 try { 401 // setKeyEntry with a private key loaded from AndroidKeyStore replaces 402 // the certificate components without touching the private key if 403 // the alias is the same as that of the private key. 404 mKeyStore.setKeyEntry(alias, privateKey, null, certsArray); 405 } catch (KeyStoreException e) { 406 Log.e(TAG, "Failed update key certificates.", e); 407 return false; 408 } 409 410 if (Log.isLoggable(TAG, Log.DEBUG)) { 411 Log.d(TAG, String.format("Set certificate for key alias %s : user %s CA chain: %s", 412 alias, emptyOrBase64Encoded(userCertificate), 413 emptyOrBase64Encoded(userCertificateChain))); 414 } 415 broadcastKeychainChange(); 416 broadcastLegacyStorageChange(); 417 return true; 418 } 419 420 private void validateAlias(String alias) { 421 if (alias == null) { 422 throw new NullPointerException("alias == null"); 423 } 424 } 425 426 private boolean hasGrant(String alias, CallerIdentity caller) { 427 return hasGrant(alias, caller.mUid); 428 } 429 430 private boolean hasGrant(String alias, int targetUid) { 431 validateAlias(alias); 432 433 if (!mGrantsDb.hasGrant(targetUid, alias)) { 434 Log.w(TAG, String.format( 435 "uid %d doesn't have permission to access the requested alias %s", 436 targetUid, alias)); 437 return false; 438 } 439 440 return true; 441 } 442 443 @Override public String installCaCertificate(byte[] caCertificate) { 444 final CallerIdentity caller = getCaller(); 445 Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller), 446 MSG_NOT_SYSTEM_OR_CERT_INSTALLER); 447 final String alias; 448 String subject = null; 449 final boolean isSecurityLoggingEnabled = mInjector.isSecurityLoggingEnabled(); 450 final X509Certificate cert; 451 try { 452 cert = parseCertificate(caCertificate); 453 454 final boolean isDebugLoggable = Log.isLoggable(TAG, Log.DEBUG); 455 subject = cert.getSubjectX500Principal().getName(X500Principal.CANONICAL); 456 if (isDebugLoggable) { 457 Log.d(TAG, String.format("Installing CA certificate: %s", subject)); 458 } 459 460 synchronized (mTrustedCertificateStore) { 461 mTrustedCertificateStore.installCertificate(cert); 462 alias = mTrustedCertificateStore.getCertificateAlias(cert); 463 } 464 } catch (IOException | CertificateException e) { 465 Log.w(TAG, "Failed installing CA certificate", e); 466 if (isSecurityLoggingEnabled && subject != null) { 467 mInjector.writeSecurityEvent( 468 TAG_CERT_AUTHORITY_INSTALLED, 0 /*result*/, subject, 469 UserHandle.myUserId()); 470 } 471 throw new IllegalStateException(e); 472 } 473 if (isSecurityLoggingEnabled && subject != null) { 474 mInjector.writeSecurityEvent( 475 TAG_CERT_AUTHORITY_INSTALLED, 1 /*result*/, subject, 476 UserHandle.myUserId()); 477 } 478 479 // If the caller is the cert installer, install the CA certificate into KeyStore. 480 // This is a temporary solution to enable CA certificates to be used as VPN trust 481 // anchors. Ultimately, the user should explicitly choose to install the VPN trust 482 // anchor separately and independently of CA certificates, at which point this code 483 // should be removed. 484 if (CERT_INSTALLER_PACKAGE.equals(caller.mPackageName)) { 485 try { 486 mKeyStore.setCertificateEntry(String.format("%s %s", subject, alias), cert); 487 } catch(KeyStoreException e) { 488 Log.e(TAG, String.format( 489 "Attempted installing %s (subject: %s) to KeyStore. Failed", alias, 490 subject), e); 491 } 492 } 493 494 broadcastLegacyStorageChange(); 495 broadcastTrustStoreChange(); 496 return alias; 497 } 498 499 /** 500 * Install a key pair to the keystore. 501 * 502 * @param privateKey The private key associated with the client certificate 503 * @param userCertificate The client certificate to be installed 504 * @param userCertificateChain The rest of the chain for the client certificate 505 * @param alias The alias under which the key pair is installed. It is invalid to pass 506 * {@code KeyChain.KEY_ALIAS_SELECTION_DENIED}. 507 * @param uid Can be only one of two values: Either 508 * {@code android.security.keystore.KeyProperties.UID_SELF} to indicate 509 * installation into the current user's system Keystore instance, or {@code 510 * Process.WIFI_UID} to indicate installation into the main user's WiFi Keystore 511 * instance. It is only valid to pass {@code Process.WIFI_UID} to the KeyChain 512 * service on user 0. 513 * @return Whether the operation succeeded or not. 514 */ 515 @Override public boolean installKeyPair(@Nullable byte[] privateKey, 516 @Nullable byte[] userCertificate, @Nullable byte[] userCertificateChain, 517 String alias, int uid) { 518 final CallerIdentity caller = getCaller(); 519 Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller), 520 MSG_NOT_SYSTEM_OR_CERT_INSTALLER); 521 if (KeyChain.KEY_ALIAS_SELECTION_DENIED.equals(alias)) { 522 throw new IllegalArgumentException("The alias specified for the key denotes " 523 + "a reserved value and cannot be used to name a key"); 524 } 525 if (!ALLOWED_UIDS.contains(uid)) { 526 Log.e(TAG, 527 String.format("Installing alias %s as UID %d is now allowed.", alias, uid)); 528 return false; 529 } 530 531 if (privateKey == null && userCertificate == null && userCertificateChain == null) { 532 Log.e(TAG, String.format("Nothing to install for alias %s", alias)); 533 return false; 534 } 535 536 if (uid == Process.WIFI_UID && UserHandle.myUserId() != UserHandle.USER_SYSTEM) { 537 Log.e(TAG, String.format( 538 "Installation into the WiFi Keystore should be called from the primary " 539 + "user, not user %d", 540 UserHandle.myUserId())); 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