From baefdcf9eb6f1ee9bf45b65b5e319be174e60735 Mon Sep 17 00:00:00 2001 From: Robin Lee Date: Wed, 26 Aug 2015 10:57:44 +0100 Subject: [PATCH] Show installed user keys in Security Settings This makes removing a particular keyset possible without deleting the whole keystore along with it. Bug: 22541933 Change-Id: I248803cb27efdd1695438dfc82951887dc579f99 --- res/layout/user_credential.xml | 64 ++++++ res/layout/user_credential_dialog.xml | 30 +++ res/layout/user_credentials.xml | 33 +++ res/values/strings.xml | 15 ++ res/xml/security_settings_misc.xml | 6 + .../settings/UserCredentialsSettings.java | 197 ++++++++++++++++++ 6 files changed, 345 insertions(+) create mode 100644 res/layout/user_credential.xml create mode 100644 res/layout/user_credential_dialog.xml create mode 100644 res/layout/user_credentials.xml create mode 100644 src/com/android/settings/UserCredentialsSettings.java diff --git a/res/layout/user_credential.xml b/res/layout/user_credential.xml new file mode 100644 index 00000000000..905822dc2de --- /dev/null +++ b/res/layout/user_credential.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + diff --git a/res/layout/user_credential_dialog.xml b/res/layout/user_credential_dialog.xml new file mode 100644 index 00000000000..032cd847184 --- /dev/null +++ b/res/layout/user_credential_dialog.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/res/layout/user_credentials.xml b/res/layout/user_credentials.xml new file mode 100644 index 00000000000..c9ff51e22ab --- /dev/null +++ b/res/layout/user_credentials.xml @@ -0,0 +1,33 @@ + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index b4a7a7407a5..c6b1b8da361 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4582,6 +4582,10 @@ Trusted credentials Display trusted CA certificates + + User credentials + + View and modify stored credentials Advanced @@ -5425,6 +5429,17 @@ Permanently remove the user CA certificate? + + one user key + + one user certificate + + one CA certificate + + %d CA certificates + + Credential Details + Spell checker diff --git a/res/xml/security_settings_misc.xml b/res/xml/security_settings_misc.xml index b67e1b7c394..a010a88b92b 100644 --- a/res/xml/security_settings_misc.xml +++ b/res/xml/security_settings_misc.xml @@ -75,6 +75,12 @@ android:persistent="false" android:fragment="com.android.settings.TrustedCredentialsSettings"/> + + parent, View view, int position, long id) { + final Credential item = (Credential) parent.getItemAtPosition(position); + + View root = getActivity().getLayoutInflater() + .inflate(R.layout.user_credential_dialog, null); + ViewGroup infoContainer = (ViewGroup) root.findViewById(R.id.credential_container); + infoContainer.addView(parent.getAdapter().getView(position, null, null)); + + new AlertDialog.Builder(getActivity()) + .setView(root) + .setTitle(R.string.user_credential_title) + .setPositiveButton(R.string.done, null) + .setNegativeButton(R.string.trusted_credentials_remove_label, + new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int id) { + final KeyStore ks = KeyStore.getInstance(); + Credentials.deleteAllTypesForAlias(ks, item.alias); + new AliasLoader().execute(); + dialog.dismiss(); + } + }) + .show(); + } + + /** + * Opens a background connection to KeyStore to list user credentials. + * The credentials are stored in a {@link CredentialAdapter} attached to the main + * {@link ListView} in the fragment. + */ + private class AliasLoader extends AsyncTask { + @Override + protected ListAdapter doInBackground(Void... params) { + // Create a list of names for credential sets, ordered by name. + SortedMap credentials = new TreeMap<>(); + KeyStore keyStore = KeyStore.getInstance(); + for (final Credential.Type type : Credential.Type.values()) { + for (final String alias : keyStore.list(type.prefix)) { + Credential c = credentials.get(alias); + if (c == null) { + credentials.put(alias, (c = new Credential(alias))); + } + c.storedTypes.add(type); + } + } + + // Flatten to array so that the list can be presented via ArrayAdapter. + Credential[] results = credentials.values().toArray(new Credential[0]); + return new CredentialAdapter(getActivity(), R.layout.user_credential, results); + } + + @Override + protected void onPostExecute(ListAdapter credentials) { + mListView.setAdapter(credentials); + } + } + + /** + * Helper class to display {@link Credential}s in a list. + */ + private static class CredentialAdapter extends ArrayAdapter { + public CredentialAdapter(Context context, int resource, Credential[] objects) { + super(context, resource, objects); + } + + @Override + public View getView(int position, View view, ViewGroup parent) { + if (view == null) { + view = LayoutInflater.from(getContext()) + .inflate(R.layout.user_credential, parent, false); + } + Credential item = getItem(position); + ((TextView) view.findViewById(R.id.alias)).setText(item.alias); + view.findViewById(R.id.contents_userkey).setVisibility( + item.storedTypes.contains(Credential.Type.USER_PRIVATE_KEY) ? VISIBLE : GONE); + view.findViewById(R.id.contents_usercrt).setVisibility( + item.storedTypes.contains(Credential.Type.USER_CERTIFICATE) ? VISIBLE : GONE); + view.findViewById(R.id.contents_cacrt).setVisibility( + item.storedTypes.contains(Credential.Type.CA_CERTIFICATE) ? VISIBLE : GONE); + return view; + } + } + + private static class Credential { + private static enum Type { + CA_CERTIFICATE (Credentials.CA_CERTIFICATE), + USER_CERTIFICATE (Credentials.USER_CERTIFICATE), + USER_PRIVATE_KEY (Credentials.USER_PRIVATE_KEY), + USER_SECRET_KEY (Credentials.USER_SECRET_KEY); + + final String prefix; + + Type(String prefix) { + this.prefix = prefix; + } + } + + /** + * Main part of the credential's alias. To fetch an item from KeyStore, prepend one of the + * prefixes from {@link CredentialItem.storedTypes}. + */ + final String alias; + + /** + * Should contain some non-empty subset of: + *
    + *
  • {@link Credentials.CA_CERTIFICATE}
  • + *
  • {@link Credentials.USER_CERTIFICATE}
  • + *
  • {@link Credentials.USER_PRIVATE_KEY}
  • + *
  • {@link Credentials.USER_SECRET_KEY}
  • + *
+ */ + final Set storedTypes = EnumSet.noneOf(Type.class); + + Credential(final String alias) { + this.alias = alias; + } + } +}