Merge "CredentialStorage: Install keys using KeyChain"
This commit is contained in:
@@ -31,6 +31,7 @@ import android.os.RemoteException;
|
|||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.security.Credentials;
|
import android.security.Credentials;
|
||||||
|
import android.security.IKeyChainService;
|
||||||
import android.security.KeyChain;
|
import android.security.KeyChain;
|
||||||
import android.security.KeyChain.KeyChainConnection;
|
import android.security.KeyChain.KeyChainConnection;
|
||||||
import android.security.KeyStore;
|
import android.security.KeyStore;
|
||||||
@@ -42,18 +43,10 @@ import androidx.appcompat.app.AlertDialog;
|
|||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
import com.android.internal.widget.LockPatternUtils;
|
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.R;
|
||||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||||
import com.android.settings.vpn2.VpnUtils;
|
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.
|
* 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.
|
* Install credentials if available, otherwise do nothing.
|
||||||
*
|
*
|
||||||
@@ -165,56 +144,18 @@ public final class CredentialStorage extends FragmentActivity {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean shouldFinish = true;
|
String alias = bundle.getString(Credentials.EXTRA_USER_KEY_ALIAS, null);
|
||||||
if (bundle.containsKey(Credentials.EXTRA_USER_PRIVATE_KEY_NAME)) {
|
if (TextUtils.isEmpty(alias)) {
|
||||||
final String key = bundle.getString(Credentials.EXTRA_USER_PRIVATE_KEY_NAME);
|
Log.e(TAG, "Cannot install key without an alias");
|
||||||
final byte[] value = bundle.getByteArray(Credentials.EXTRA_USER_PRIVATE_KEY_DATA);
|
return true;
|
||||||
|
|
||||||
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;
|
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();
|
||||||
|
|
||||||
if (bundle.containsKey(Credentials.EXTRA_USER_CERTIFICATE_NAME)) {
|
return false;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the broadcast.
|
|
||||||
final Intent broadcast = new Intent(KeyChain.ACTION_KEYCHAIN_CHANGED);
|
|
||||||
sendBroadcast(broadcast);
|
|
||||||
|
|
||||||
setResult(RESULT_OK);
|
|
||||||
return shouldFinish;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -308,26 +249,45 @@ public final class CredentialStorage extends FragmentActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Background task to mark a given key alias as user-selectable, so that
|
* Background task to install a certificate into KeyChain or the WiFi Keystore.
|
||||||
* it can be selected by users from the Certificate Selection prompt.
|
|
||||||
*/
|
*/
|
||||||
private class MarkKeyAsUserSelectable extends AsyncTask<Void, Void, Boolean> {
|
private class InstallKeyInKeyChain extends AsyncTask<Void, Void, Boolean> {
|
||||||
final String mAlias;
|
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;
|
mAlias = alias;
|
||||||
|
mKeyData = keyData;
|
||||||
|
mCertData = certData;
|
||||||
|
mCaListData = caListData;
|
||||||
|
mUid = uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(Void... unused) {
|
protected Boolean doInBackground(Void... unused) {
|
||||||
try (KeyChainConnection keyChainConnection = KeyChain.bind(CredentialStorage.this)) {
|
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;
|
return true;
|
||||||
} catch (RemoteException e) {
|
} 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;
|
return false;
|
||||||
} catch (InterruptedException e) {
|
} 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();
|
Thread.currentThread().interrupt();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -335,12 +295,28 @@ public final class CredentialStorage extends FragmentActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Boolean result) {
|
protected void onPostExecute(Boolean result) {
|
||||||
Log.i(TAG, String.format("Marked alias %s as selectable, success? %s",
|
CredentialStorage.this.onKeyInstalled(mAlias, mUid, result);
|
||||||
mAlias, result));
|
|
||||||
CredentialStorage.this.finish();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
* Check that the caller is either certinstaller or Settings running in a profile of this user.
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user