summaryrefslogtreecommitdiffstats
path: root/src/com/android/settings/security
diff options
context:
space:
mode:
authorEran Messeri <eranm@google.com>2019-09-10 17:40:28 +0100
committerEran Messeri <eranm@google.com>2019-09-12 15:54:30 +0100
commitd83c190f2957a67c1df398a86ab5116da7002c0a (patch)
treefb4a1b040d1b1cc03848e883f013d9df9e660832 /src/com/android/settings/security
parentce024a0dc5376522b29639da60460a0cfc1fd52c (diff)
downloadpackages_apps_Settings-d83c190f2957a67c1df398a86ab5116da7002c0a.tar.gz
packages_apps_Settings-d83c190f2957a67c1df398a86ab5116da7002c0a.tar.bz2
packages_apps_Settings-d83c190f2957a67c1df398a86ab5116da7002c0a.zip
CredentialStorage: Install keys using KeyChain
This is part of the work to unify the manual certificate installation flow (via "Install from storage" in the Settings app) with the programmatic one (using DevicePolicyManager.installKeyPair). The change to CredentialStorage is the crux of this work, where the key is no longer installed by calling Keystore directly. Instead, a new AsyncTask, InstallKeyInKeyChain, was created, which calls KeyChainService.installKeyPair with the key data and associated certificates (as well as the UID of the designated service, to allow installation into the WiFi Keystore). Once that task completes, if the key was installed successfully, then it is marked as user-selectable. Test: Manual CtsVerifier tests: KeyChain Storage Test, CA Cert Notification Test Test: cts-tradefed run commandAndExit cts-dev -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.MixedDeviceOwnerTest#testKeyManagement Bug: 138375478 Change-Id: I7c4b4ea725a34307f58d27252c2958771001636f
Diffstat (limited to 'src/com/android/settings/security')
-rw-r--r--src/com/android/settings/security/CredentialStorage.java134
1 files changed, 55 insertions, 79 deletions
diff --git a/src/com/android/settings/security/CredentialStorage.java b/src/com/android/settings/security/CredentialStorage.java
index 0ea37b5bc7..5e64723085 100644
--- a/src/com/android/settings/security/CredentialStorage.java
+++ b/src/com/android/settings/security/CredentialStorage.java
@@ -31,6 +31,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.security.Credentials;
+import android.security.IKeyChainService;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
import android.security.KeyStore;
@@ -42,18 +43,10 @@ import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import com.android.internal.widget.LockPatternUtils;
-import com.android.org.bouncycastle.asn1.ASN1InputStream;
-import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import com.android.settings.R;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.vpn2.VpnUtils;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-import sun.security.util.ObjectIdentifier;
-import sun.security.x509.AlgorithmId;
-
/**
* CredentialStorage handles resetting and installing keys into KeyStore.
*/
@@ -118,20 +111,6 @@ public final class CredentialStorage extends FragmentActivity {
}
}
- private boolean isHardwareBackedKey(byte[] keyData) {
- try {
- final ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(keyData));
- final PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
- final String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
- final String algName = new AlgorithmId(new ObjectIdentifier(algOid)).getName();
-
- return KeyChain.isBoundKeyAlgorithm(algName);
- } catch (IOException e) {
- Log.e(TAG, "Failed to parse key data");
- return false;
- }
- }
-
/**
* Install credentials if available, otherwise do nothing.
*
@@ -165,56 +144,18 @@ public final class CredentialStorage extends FragmentActivity {
return true;
}
- boolean shouldFinish = true;
- if (bundle.containsKey(Credentials.EXTRA_USER_PRIVATE_KEY_NAME)) {
- final String key = bundle.getString(Credentials.EXTRA_USER_PRIVATE_KEY_NAME);
- final byte[] value = bundle.getByteArray(Credentials.EXTRA_USER_PRIVATE_KEY_DATA);
-
- if (!mKeyStore.importKey(key, value, uid, KeyStore.FLAG_NONE)) {
- Log.e(TAG, "Failed to install " + key + " as uid " + uid);
- return true;
- }
- // The key was prepended USER_PRIVATE_KEY by the CredentialHelper. However,
- // KeyChain internally uses the raw alias name and only prepends USER_PRIVATE_KEY
- // to the key name when interfacing with KeyStore.
- // This is generally a symptom of CredentialStorage and CredentialHelper relying
- // on internal implementation details of KeyChain and imitating its functionality
- // rather than delegating to KeyChain for the certificate installation.
- if (uid == Process.SYSTEM_UID || uid == KeyStore.UID_SELF) {
- new MarkKeyAsUserSelectable(
- key.replaceFirst("^" + Credentials.USER_PRIVATE_KEY, "")).execute();
- shouldFinish = false;
- }
- }
-
- final int flags = KeyStore.FLAG_NONE;
-
- if (bundle.containsKey(Credentials.EXTRA_USER_CERTIFICATE_NAME)) {
- final String certName = bundle.getString(Credentials.EXTRA_USER_CERTIFICATE_NAME);
- final byte[] certData = bundle.getByteArray(Credentials.EXTRA_USER_CERTIFICATE_DATA);
-
- if (!mKeyStore.put(certName, certData, uid, flags)) {
- Log.e(TAG, "Failed to install " + certName + " as uid " + uid);
- return shouldFinish;
- }
- }
-
- if (bundle.containsKey(Credentials.EXTRA_CA_CERTIFICATES_NAME)) {
- final String caListName = bundle.getString(Credentials.EXTRA_CA_CERTIFICATES_NAME);
- final byte[] caListData = bundle.getByteArray(Credentials.EXTRA_CA_CERTIFICATES_DATA);
-
- if (!mKeyStore.put(caListName, caListData, uid, flags)) {
- Log.e(TAG, "Failed to install " + caListName + " as uid " + uid);
- return shouldFinish;
- }
+ String alias = bundle.getString(Credentials.EXTRA_USER_KEY_ALIAS, null);
+ if (TextUtils.isEmpty(alias)) {
+ Log.e(TAG, "Cannot install key without an alias");
+ return true;
}
- // Send the broadcast.
- final Intent broadcast = new Intent(KeyChain.ACTION_KEYCHAIN_CHANGED);
- sendBroadcast(broadcast);
+ final byte[] privateKeyData = bundle.getByteArray(Credentials.EXTRA_USER_PRIVATE_KEY_DATA);
+ final byte[] certData = bundle.getByteArray(Credentials.EXTRA_USER_CERTIFICATE_DATA);
+ final byte[] caListData = bundle.getByteArray(Credentials.EXTRA_CA_CERTIFICATES_DATA);
+ new InstallKeyInKeyChain(alias, privateKeyData, certData, caListData, uid).execute();
- setResult(RESULT_OK);
- return shouldFinish;
+ return false;
}
/**
@@ -308,26 +249,45 @@ public final class CredentialStorage extends FragmentActivity {
}
/**
- * Background task to mark a given key alias as user-selectable, so that
- * it can be selected by users from the Certificate Selection prompt.
+ * Background task to install a certificate into KeyChain or the WiFi Keystore.
*/
- private class MarkKeyAsUserSelectable extends AsyncTask<Void, Void, Boolean> {
+ private class InstallKeyInKeyChain extends AsyncTask<Void, Void, Boolean> {
final String mAlias;
+ private final byte[] mKeyData;
+ private final byte[] mCertData;
+ private final byte[] mCaListData;
+ private final int mUid;
- MarkKeyAsUserSelectable(String alias) {
+ InstallKeyInKeyChain(String alias, byte[] keyData, byte[] certData, byte[] caListData,
+ int uid) {
mAlias = alias;
+ mKeyData = keyData;
+ mCertData = certData;
+ mCaListData = caListData;
+ mUid = uid;
}
@Override
protected Boolean doInBackground(Void... unused) {
try (KeyChainConnection keyChainConnection = KeyChain.bind(CredentialStorage.this)) {
- keyChainConnection.getService().setUserSelectable(mAlias, true);
+ IKeyChainService service = keyChainConnection.getService();
+ if (!service.installKeyPair(mKeyData, mCertData, mCaListData, mAlias, mUid)) {
+ Log.w(TAG, String.format("Failed installing key %s", mAlias));
+ return false;
+ }
+
+ // If this is not a WiFi key, mark it as user-selectable, so that it can be
+ // selected by users from the Certificate Selection prompt.
+ if (mUid == Process.SYSTEM_UID || mUid == KeyStore.UID_SELF) {
+ service.setUserSelectable(mAlias, true);
+ }
+
return true;
} catch (RemoteException e) {
- Log.w(TAG, "Failed to mark key " + mAlias + " as user-selectable.");
+ Log.w(TAG, String.format("Failed to install key %s to uid %d", mAlias, mUid), e);
return false;
} catch (InterruptedException e) {
- Log.w(TAG, "Failed to mark key " + mAlias + " as user-selectable.");
+ Log.w(TAG, String.format("Interrupted while installing key %s", mAlias), e);
Thread.currentThread().interrupt();
return false;
}
@@ -335,12 +295,28 @@ public final class CredentialStorage extends FragmentActivity {
@Override
protected void onPostExecute(Boolean result) {
- Log.i(TAG, String.format("Marked alias %s as selectable, success? %s",
- mAlias, result));
- CredentialStorage.this.finish();
+ CredentialStorage.this.onKeyInstalled(mAlias, mUid, result);
}
}
+ private void onKeyInstalled(String alias, int uid, boolean result) {
+ if (!result) {
+ Log.w(TAG, String.format("Error installing alias %s for uid %d", alias, uid));
+ finish();
+ return;
+ }
+
+ Log.i(TAG, String.format("Successfully installed alias %s to uid %d.",
+ alias, uid));
+
+ // Send the broadcast.
+ final Intent broadcast = new Intent(KeyChain.ACTION_KEYCHAIN_CHANGED);
+ sendBroadcast(broadcast);
+ setResult(RESULT_OK);
+
+ finish();
+ }
+
/**
* Check that the caller is either certinstaller or Settings running in a profile of this user.
*/