Merge "Keystore 2.0: Update credential settings to use public Keystore API." am: 7086917968
Original change: https://android-review.googlesource.com/c/platform/packages/apps/Settings/+/1569700
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: I31b4a6e88530092dc2849dc4fb7e09c0d5463db3
diff --git a/src/com/android/settings/UserCredentialsSettings.java b/src/com/android/settings/UserCredentialsSettings.java
index d322819..5f72ca5 100644
--- a/src/com/android/settings/UserCredentialsSettings.java
+++ b/src/com/android/settings/UserCredentialsSettings.java
@@ -34,9 +34,9 @@
import android.security.IKeyChainService;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
-import android.security.KeyStore;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.AndroidKeyStoreProvider;
+import android.security.keystore.KeyProperties;
+import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
@@ -55,13 +55,21 @@
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.EnumSet;
+import java.util.Enumeration;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
+import javax.crypto.SecretKey;
+
public class UserCredentialsSettings extends SettingsPreferenceFragment
implements View.OnClickListener {
private static final String TAG = "UserCredentialsSettings";
@@ -201,21 +209,19 @@
}
private void deleteWifiCredential(final Credential credential) {
- final KeyStore keyStore = KeyStore.getInstance();
- final EnumSet<Credential.Type> storedTypes = credential.getStoredTypes();
-
- // Remove all Wi-Fi credentials
- if (storedTypes.contains(Credential.Type.USER_KEY)) {
- keyStore.delete(Credentials.USER_PRIVATE_KEY + credential.getAlias(),
- Process.WIFI_UID);
- }
- if (storedTypes.contains(Credential.Type.USER_CERTIFICATE)) {
- keyStore.delete(Credentials.USER_CERTIFICATE + credential.getAlias(),
- Process.WIFI_UID);
- }
- if (storedTypes.contains(Credential.Type.CA_CERTIFICATE)) {
- keyStore.delete(Credentials.CA_CERTIFICATE + credential.getAlias(),
- Process.WIFI_UID);
+ try {
+ KeyStore keyStore = null;
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(
+ new AndroidKeyStoreLoadStoreParameter(
+ KeyProperties.NAMESPACE_WIFI));
+ } else {
+ keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(Process.WIFI_UID);
+ }
+ keyStore.deleteEntry(credential.getAlias());
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to delete keys from keystore.");
}
}
@@ -266,73 +272,103 @@
*/
@Override
protected List<Credential> doInBackground(Void... params) {
- final KeyStore keyStore = KeyStore.getInstance();
-
// Certificates can be installed into SYSTEM_UID or WIFI_UID through CertInstaller.
final int myUserId = UserHandle.myUserId();
final int systemUid = UserHandle.getUid(myUserId, Process.SYSTEM_UID);
final int wifiUid = UserHandle.getUid(myUserId, Process.WIFI_UID);
- List<Credential> credentials = new ArrayList<>();
- credentials.addAll(getCredentialsForUid(keyStore, systemUid).values());
- credentials.addAll(getCredentialsForUid(keyStore, wifiUid).values());
- return credentials;
- }
+ try {
+ KeyStore processKeystore = KeyStore.getInstance("AndroidKeyStore");
+ processKeystore.load(null);
+ KeyStore wifiKeystore = null;
+ if (myUserId == 0) {
+ // Only the primary user may see wifi configurations.
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ wifiKeystore = KeyStore.getInstance("AndroidKeyStore");
+ wifiKeystore.load(new AndroidKeyStoreLoadStoreParameter(
+ KeyProperties.NAMESPACE_WIFI));
+ } else {
+ wifiKeystore = AndroidKeyStoreProvider.getKeyStoreForUid(Process.WIFI_UID);
+ }
+ }
- private boolean isAsymmetric(KeyStore keyStore, String alias, int uid)
- throws UnrecoverableKeyException {
- KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
- int errorCode = keyStore.getKeyCharacteristics(alias, null, null, uid,
- keyCharacteristics);
- if (errorCode != KeyStore.NO_ERROR) {
- throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to obtain information about key")
- .initCause(KeyStore.getKeyStoreException(errorCode));
+ List<Credential> credentials = new ArrayList<>();
+ credentials.addAll(getCredentialsForUid(processKeystore, systemUid).values());
+ if (wifiKeystore != null) {
+ credentials.addAll(getCredentialsForUid(wifiKeystore, wifiUid).values());
}
- Integer keymasterAlgorithm = keyCharacteristics.getEnum(
- KeymasterDefs.KM_TAG_ALGORITHM);
- if (keymasterAlgorithm == null) {
- throw new UnrecoverableKeyException("Key algorithm unknown");
- }
- return keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
- keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC;
+ return credentials;
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to load credentials from Keystore.", e);
+ }
}
private SortedMap<String, Credential> getCredentialsForUid(KeyStore keyStore, int uid) {
- final SortedMap<String, Credential> aliasMap = new TreeMap<>();
- for (final Credential.Type type : Credential.Type.values()) {
- for (final String prefix : type.prefix) {
- for (final String alias : keyStore.list(prefix, uid)) {
- if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
+ try {
+ final SortedMap<String, Credential> aliasMap = new TreeMap<>();
+ boolean isSystem = UserHandle.getAppId(uid) == Process.SYSTEM_UID;
+ Enumeration<String> aliases = keyStore.aliases();
+ while (aliases.hasMoreElements()) {
+ String alias = aliases.nextElement();
+ Credential c = new Credential(alias, uid);
+ Key key = null;
+ try {
+ key = keyStore.getKey(alias, null);
+ } catch (NoSuchAlgorithmException | UnrecoverableKeyException e) {
+ Log.e(TAG, "Error tying to retrieve key: " + alias, e);
+ continue;
+ }
+ if (key != null) {
+ // So we have a key
+ if (key instanceof SecretKey) {
+ // We don't display any symmetric key entries.
+ continue;
+ }
+ if (isSystem) {
// Do not show work profile keys in user credentials
if (alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT) ||
alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT)) {
continue;
}
// Do not show synthetic password keys in user credential
+ // We should never reach this point because the synthetic password key
+ // is symmetric.
if (alias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) {
continue;
}
}
- try {
- if (type == Credential.Type.USER_KEY &&
- !isAsymmetric(keyStore, prefix + alias, uid)) {
- continue;
+ // At this point we have determined that we have an asymmetric key.
+ // so we have at least a USER_KEY and USER_CERTIFICATE.
+ c.storedTypes.add(Credential.Type.USER_KEY);
+
+ Certificate[] certs = keyStore.getCertificateChain(alias);
+ if (certs != null) {
+ c.storedTypes.add(Credential.Type.USER_CERTIFICATE);
+ if (certs.length > 1) {
+ c.storedTypes.add(Credential.Type.CA_CERTIFICATE);
}
- } catch (UnrecoverableKeyException e) {
- Log.e(TAG, "Unable to determine algorithm of key: " + prefix + alias, e);
- continue;
}
- Credential c = aliasMap.get(alias);
- if (c == null) {
- c = new Credential(alias, uid);
- aliasMap.put(alias, c);
+ } else {
+ // So there is no key but we have an alias. This must mean that we have
+ // some certificate.
+ if (keyStore.isCertificateEntry(alias)) {
+ c.storedTypes.add(Credential.Type.CA_CERTIFICATE);
+ } else {
+ // This is a weired inconsistent case that should not exist.
+ // Pure trusted certificate entries should be stored in CA_CERTIFICATE,
+ // but if isCErtificateEntry returns null this means that only the
+ // USER_CERTIFICATE is populated which should never be the case without
+ // a private key. It can still be retrieved with
+ // keystore.getCertificate().
+ c.storedTypes.add(Credential.Type.USER_CERTIFICATE);
}
- c.storedTypes.add(type);
}
+ aliasMap.put(alias, c);
}
+ return aliasMap;
+ } catch (KeyStoreException e) {
+ throw new RuntimeException("Failed to load credential from Android Keystore.", e);
}
- return aliasMap;
}
@Override
diff --git a/src/com/android/settings/security/CredentialStorage.java b/src/com/android/settings/security/CredentialStorage.java
index 5e64723..7b9f419 100644
--- a/src/com/android/settings/security/CredentialStorage.java
+++ b/src/com/android/settings/security/CredentialStorage.java
@@ -63,7 +63,6 @@
private static final int CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST = 1;
- private final KeyStore mKeyStore = KeyStore.getInstance();
private LockPatternUtils mUtils;
/**
diff --git a/src/com/android/settings/security/ResetCredentialsPreferenceController.java b/src/com/android/settings/security/ResetCredentialsPreferenceController.java
index 0700b46..48e7f84 100644
--- a/src/com/android/settings/security/ResetCredentialsPreferenceController.java
+++ b/src/com/android/settings/security/ResetCredentialsPreferenceController.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.os.UserManager;
-import android.security.KeyStore;
import androidx.preference.PreferenceScreen;
@@ -27,6 +26,9 @@
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnResume;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+
public class ResetCredentialsPreferenceController extends RestrictedEncryptionPreferenceController
implements LifecycleObserver, OnResume {
@@ -38,7 +40,13 @@
public ResetCredentialsPreferenceController(Context context, Lifecycle lifecycle) {
super(context, UserManager.DISALLOW_CONFIG_CREDENTIALS);
- mKeyStore = KeyStore.getInstance();
+ KeyStore keyStore = null;
+ try {
+ keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ } catch (Exception e) {
+ }
+ mKeyStore = keyStore;
if (lifecycle != null) {
lifecycle.addObserver(this);
}
@@ -58,7 +66,15 @@
@Override
public void onResume() {
if (mPreference != null && !mPreference.isDisabledByAdmin()) {
- mPreference.setEnabled(!mKeyStore.isEmpty());
+ boolean isEnabled = false;
+ try {
+ if (mKeyStore != null) {
+ isEnabled = mKeyStore.aliases().hasMoreElements();
+ }
+ } catch (KeyStoreException e) {
+ // If access to keystore fails, treat as disabled.
+ }
+ mPreference.setEnabled(isEnabled);
}
}
}