Loading src/com/android/settings/UserCredentialsSettings.java +95 −59 Original line number Diff line number Diff line Loading @@ -34,9 +34,9 @@ import android.security.Credentials; 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; Loading @@ -55,13 +55,21 @@ import com.android.settingslib.RestrictedLockUtils; 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"; Loading Loading @@ -201,21 +209,19 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment } 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); 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); } if (storedTypes.contains(Credential.Type.CA_CERTIFICATE)) { keyStore.delete(Credentials.CA_CERTIFICATE + credential.getAlias(), Process.WIFI_UID); keyStore.deleteEntry(credential.getAlias()); } catch (Exception e) { throw new RuntimeException("Failed to delete keys from keystore."); } } Loading Loading @@ -266,73 +272,103 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment */ @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 credentials; } catch (Exception e) { throw new RuntimeException("Failed to load credentials from Keystore.", e); } return keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA || keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC; } private SortedMap<String, Credential> getCredentialsForUid(KeyStore keyStore, int uid) { try { 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) { 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; } } catch (UnrecoverableKeyException e) { Log.e(TAG, "Unable to determine algorithm of key: " + prefix + alias, e); 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); } Credential c = aliasMap.get(alias); if (c == null) { c = new Credential(alias, uid); aliasMap.put(alias, c); } c.storedTypes.add(type); } 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); } } aliasMap.put(alias, c); } return aliasMap; } catch (KeyStoreException e) { throw new RuntimeException("Failed to load credential from Android Keystore.", e); } } @Override Loading src/com/android/settings/security/CredentialStorage.java +0 −1 Original line number Diff line number Diff line Loading @@ -63,7 +63,6 @@ public final class CredentialStorage extends FragmentActivity { private static final int CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST = 1; private final KeyStore mKeyStore = KeyStore.getInstance(); private LockPatternUtils mUtils; /** Loading src/com/android/settings/security/ResetCredentialsPreferenceController.java +19 −3 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.settings.security; import android.content.Context; import android.os.UserManager; import android.security.KeyStore; import androidx.preference.PreferenceScreen; Loading @@ -27,6 +26,9 @@ import com.android.settingslib.core.lifecycle.Lifecycle; 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 { Loading @@ -38,7 +40,13 @@ public class ResetCredentialsPreferenceController extends RestrictedEncryptionPr 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); } Loading @@ -58,7 +66,15 @@ public class ResetCredentialsPreferenceController extends RestrictedEncryptionPr @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); } } } Loading
src/com/android/settings/UserCredentialsSettings.java +95 −59 Original line number Diff line number Diff line Loading @@ -34,9 +34,9 @@ import android.security.Credentials; 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; Loading @@ -55,13 +55,21 @@ import com.android.settingslib.RestrictedLockUtils; 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"; Loading Loading @@ -201,21 +209,19 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment } 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); 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); } if (storedTypes.contains(Credential.Type.CA_CERTIFICATE)) { keyStore.delete(Credentials.CA_CERTIFICATE + credential.getAlias(), Process.WIFI_UID); keyStore.deleteEntry(credential.getAlias()); } catch (Exception e) { throw new RuntimeException("Failed to delete keys from keystore."); } } Loading Loading @@ -266,73 +272,103 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment */ @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 credentials; } catch (Exception e) { throw new RuntimeException("Failed to load credentials from Keystore.", e); } return keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA || keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC; } private SortedMap<String, Credential> getCredentialsForUid(KeyStore keyStore, int uid) { try { 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) { 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; } } catch (UnrecoverableKeyException e) { Log.e(TAG, "Unable to determine algorithm of key: " + prefix + alias, e); 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); } Credential c = aliasMap.get(alias); if (c == null) { c = new Credential(alias, uid); aliasMap.put(alias, c); } c.storedTypes.add(type); } 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); } } aliasMap.put(alias, c); } return aliasMap; } catch (KeyStoreException e) { throw new RuntimeException("Failed to load credential from Android Keystore.", e); } } @Override Loading
src/com/android/settings/security/CredentialStorage.java +0 −1 Original line number Diff line number Diff line Loading @@ -63,7 +63,6 @@ public final class CredentialStorage extends FragmentActivity { private static final int CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST = 1; private final KeyStore mKeyStore = KeyStore.getInstance(); private LockPatternUtils mUtils; /** Loading
src/com/android/settings/security/ResetCredentialsPreferenceController.java +19 −3 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.settings.security; import android.content.Context; import android.os.UserManager; import android.security.KeyStore; import androidx.preference.PreferenceScreen; Loading @@ -27,6 +26,9 @@ import com.android.settingslib.core.lifecycle.Lifecycle; 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 { Loading @@ -38,7 +40,13 @@ public class ResetCredentialsPreferenceController extends RestrictedEncryptionPr 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); } Loading @@ -58,7 +66,15 @@ public class ResetCredentialsPreferenceController extends RestrictedEncryptionPr @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); } } }