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