Merge "Migrate recoverablekeystore to KeyStore V2." am: dbf8a7c1bd
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1603470 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: I7f96343c65df7accaae3fb5032771dbcd05191f5
This commit is contained in:
@@ -27,8 +27,11 @@ import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.ServiceSpecificException;
|
||||
import android.security.KeyStore;
|
||||
import android.security.keystore.AndroidKeyStoreProvider;
|
||||
import android.security.KeyStore2;
|
||||
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
||||
import android.security.keystore2.AndroidKeyStoreProvider;
|
||||
import android.system.keystore2.Domain;
|
||||
import android.system.keystore2.KeyDescriptor;
|
||||
|
||||
import com.android.internal.widget.ILockSettings;
|
||||
|
||||
@@ -709,10 +712,34 @@ public class RecoveryController {
|
||||
*/
|
||||
@NonNull Key getKeyFromGrant(@NonNull String grantAlias)
|
||||
throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
|
||||
return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
|
||||
mKeyStore,
|
||||
grantAlias,
|
||||
KeyStore.UID_SELF);
|
||||
if (grantAlias.startsWith(APPLICATION_KEY_GRANT_PREFIX)) {
|
||||
return AndroidKeyStoreProvider
|
||||
.loadAndroidKeyStoreSecretKeyFromKeystore(
|
||||
KeyStore2.getInstance(),
|
||||
getGrantDescriptor(grantAlias));
|
||||
}
|
||||
// TODO(b/171305545): remove KeyStore1 logic.
|
||||
return android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
|
||||
mKeyStore,
|
||||
grantAlias,
|
||||
KeyStore.UID_SELF);
|
||||
|
||||
}
|
||||
|
||||
private static final String APPLICATION_KEY_GRANT_PREFIX = "recoverable_key:";
|
||||
|
||||
private static @Nullable KeyDescriptor getGrantDescriptor(String grantAlias) {
|
||||
KeyDescriptor result = new KeyDescriptor();
|
||||
result.domain = Domain.GRANT;
|
||||
result.blob = null;
|
||||
result.alias = null;
|
||||
try {
|
||||
result.nspace = Long.parseUnsignedLong(
|
||||
grantAlias.substring(APPLICATION_KEY_GRANT_PREFIX.length()), 16);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,6 +43,7 @@ import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
/**
|
||||
* A provider focused on providing JCA interfaces for the Android KeyStore.
|
||||
@@ -299,13 +300,26 @@ public class AndroidKeyStoreProvider extends Provider {
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide **/
|
||||
@NonNull
|
||||
public static SecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
|
||||
@NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)
|
||||
throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
|
||||
|
||||
AndroidKeyStoreKey key =
|
||||
loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor);
|
||||
if (key instanceof SecretKey) {
|
||||
return (SecretKey) key;
|
||||
} else {
|
||||
throw new UnrecoverableKeyException("No secret key found by the given alias.");
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static AndroidKeyStoreSecretKey makeAndroidKeyStoreSecretKeyFromKeyEntryResponse(
|
||||
@NonNull KeyDescriptor descriptor,
|
||||
@NonNull KeyEntryResponse response, int algorithm, int digest)
|
||||
throws UnrecoverableKeyException {
|
||||
|
||||
@KeyProperties.KeyAlgorithmEnum String keyAlgorithmString;
|
||||
try {
|
||||
keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
|
||||
@@ -337,7 +351,6 @@ public class AndroidKeyStoreProvider extends Provider {
|
||||
public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
|
||||
@NonNull KeyStore2 keyStore, @NonNull String alias, int namespace)
|
||||
throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
|
||||
|
||||
KeyDescriptor descriptor = new KeyDescriptor();
|
||||
if (namespace == KeyProperties.NAMESPACE_APPLICATION) {
|
||||
descriptor.nspace = KeyProperties.NAMESPACE_APPLICATION; // ignored;
|
||||
|
||||
@@ -489,8 +489,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return KeyStore.getInstance();
|
||||
}
|
||||
|
||||
public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) {
|
||||
return RecoverableKeyStoreManager.getInstance(mContext, keyStore);
|
||||
public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
|
||||
return RecoverableKeyStoreManager.getInstance(mContext);
|
||||
}
|
||||
|
||||
public IStorageManager getStorageManager() {
|
||||
@@ -571,7 +571,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
mInjector = injector;
|
||||
mContext = injector.getContext();
|
||||
mKeyStore = injector.getKeyStore();
|
||||
mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager(mKeyStore);
|
||||
mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
|
||||
mHandler = injector.getHandler(injector.getServiceThread());
|
||||
mStrongAuth = injector.getStrongAuth();
|
||||
mActivityManager = injector.getActivityManager();
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.server.locksettings.recoverablekeystore;
|
||||
|
||||
import android.security.keystore2.AndroidKeyStoreProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyStore;
|
||||
@@ -31,23 +29,10 @@ import java.security.cert.CertificateException;
|
||||
*/
|
||||
public class KeyStoreProxyImpl implements KeyStoreProxy {
|
||||
|
||||
public static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
|
||||
|
||||
private final KeyStore mKeyStore;
|
||||
|
||||
/**
|
||||
* TODO This function redirects keystore access to the legacy keystore during a transitional
|
||||
* phase during which not all calling code has been adjusted to use Keystore 2.0.
|
||||
* This can be reverted to a constant of "AndroidKeyStore" when b/171305684 is complete.
|
||||
* The specific bug for this component is b/171305545.
|
||||
*/
|
||||
static String androidKeystoreProviderName() {
|
||||
if (AndroidKeyStoreProvider.isInstalled()) {
|
||||
return "AndroidKeyStoreLegacy";
|
||||
} else {
|
||||
return "AndroidKeyStore";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A new instance, delegating to {@code keyStore}.
|
||||
*/
|
||||
@@ -84,7 +69,7 @@ public class KeyStoreProxyImpl implements KeyStoreProxy {
|
||||
* @throws KeyStoreException if there was a problem getting or initializing the key store.
|
||||
*/
|
||||
public static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException {
|
||||
KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName());
|
||||
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
|
||||
try {
|
||||
keyStore.load(/*param=*/ null);
|
||||
} catch (CertificateException | IOException | NoSuchAlgorithmException e) {
|
||||
|
||||
@@ -484,7 +484,7 @@ public class PlatformKeyManager {
|
||||
* @throws KeyStoreException if there was a problem getting or initializing the key store.
|
||||
*/
|
||||
private static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException {
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStoreProxyImpl.androidKeystoreProviderName());
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStoreProxyImpl.ANDROID_KEY_STORE_PROVIDER);
|
||||
try {
|
||||
keyStore.load(/*param=*/ null);
|
||||
} catch (CertificateException | IOException | NoSuchAlgorithmException e) {
|
||||
|
||||
@@ -34,7 +34,6 @@ import android.os.Binder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceSpecificException;
|
||||
import android.os.UserHandle;
|
||||
import android.security.KeyStore;
|
||||
import android.security.keystore.recovery.KeyChainProtectionParams;
|
||||
import android.security.keystore.recovery.KeyChainSnapshot;
|
||||
import android.security.keystore.recovery.RecoveryCertPath;
|
||||
@@ -110,14 +109,14 @@ public class RecoverableKeyStoreManager {
|
||||
* @hide
|
||||
*/
|
||||
public static synchronized RecoverableKeyStoreManager
|
||||
getInstance(Context context, KeyStore keystore) {
|
||||
getInstance(Context context) {
|
||||
if (mInstance == null) {
|
||||
RecoverableKeyStoreDb db = RecoverableKeyStoreDb.newInstance(context);
|
||||
PlatformKeyManager platformKeyManager;
|
||||
ApplicationKeyStorage applicationKeyStorage;
|
||||
try {
|
||||
platformKeyManager = PlatformKeyManager.getInstance(context, db);
|
||||
applicationKeyStorage = ApplicationKeyStorage.getInstance(keystore);
|
||||
applicationKeyStorage = ApplicationKeyStorage.getInstance();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// Impossible: all algorithms must be supported by AOSP
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@@ -21,9 +21,13 @@ import static android.security.keystore.recovery.RecoveryController.ERROR_SERVIC
|
||||
import android.annotation.Nullable;
|
||||
import android.os.ServiceSpecificException;
|
||||
import android.security.Credentials;
|
||||
import android.security.KeyStore;
|
||||
import android.security.KeyStore2;
|
||||
import android.security.keystore.KeyProperties;
|
||||
import android.security.keystore.KeyProtection;
|
||||
import android.security.KeyStore;
|
||||
import android.system.keystore2.Domain;
|
||||
import android.system.keystore2.KeyDescriptor;
|
||||
import android.system.keystore2.KeyPermission;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -47,32 +51,37 @@ public class ApplicationKeyStorage {
|
||||
|
||||
private static final String APPLICATION_KEY_ALIAS_PREFIX =
|
||||
"com.android.server.locksettings.recoverablekeystore/application/";
|
||||
private static final String APPLICATION_KEY_GRANT_PREFIX = "recoverable_key:";
|
||||
|
||||
private final KeyStoreProxy mKeyStore;
|
||||
private final KeyStore mKeystoreService;
|
||||
|
||||
public static ApplicationKeyStorage getInstance(KeyStore keystoreService)
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public static ApplicationKeyStorage getInstance()
|
||||
throws KeyStoreException {
|
||||
return new ApplicationKeyStorage(
|
||||
new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore()),
|
||||
keystoreService);
|
||||
new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore()));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ApplicationKeyStorage(KeyStoreProxy keyStore, KeyStore keystoreService) {
|
||||
ApplicationKeyStorage(KeyStoreProxy keyStore) {
|
||||
mKeyStore = keyStore;
|
||||
mKeystoreService = keystoreService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns grant alias, valid in Applications namespace.
|
||||
* Returns String representation of {@code KeyDescriptor} valid in application's namespace.
|
||||
*/
|
||||
public @Nullable String getGrantAlias(int userId, int uid, String alias) {
|
||||
// Aliases used by {@link KeyStore} are different than used by public API.
|
||||
// {@code USER_PRIVATE_KEY} prefix is used secret keys.
|
||||
Log.i(TAG, String.format(Locale.US, "Get %d/%d/%s", userId, uid, alias));
|
||||
String keystoreAlias = Credentials.USER_PRIVATE_KEY + getInternalAlias(userId, uid, alias);
|
||||
return mKeystoreService.grant(keystoreAlias, uid);
|
||||
String keystoreAlias = getInternalAlias(userId, uid, alias);
|
||||
if (useKeyStore2()) {
|
||||
return makeKeystoreEngineGrantString(uid, keystoreAlias);
|
||||
} else {
|
||||
// Aliases used by {@link KeyStore} are different than used by public API.
|
||||
// {@code USER_PRIVATE_KEY} prefix is used secret keys.
|
||||
return KeyStore.getInstance().grant(Credentials.USER_PRIVATE_KEY + keystoreAlias, uid);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey)
|
||||
@@ -117,4 +126,31 @@ public class ApplicationKeyStorage {
|
||||
private String getInternalAlias(int userId, int uid, String alias) {
|
||||
return APPLICATION_KEY_ALIAS_PREFIX + userId + "/" + uid + "/" + alias;
|
||||
}
|
||||
|
||||
private String makeKeystoreEngineGrantString(int uid, String alias) {
|
||||
if (alias == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
KeyDescriptor key = new KeyDescriptor();
|
||||
key.domain = Domain.APP;
|
||||
key.nspace = KeyProperties.NAMESPACE_APPLICATION;
|
||||
key.alias = alias;
|
||||
key.blob = null;
|
||||
|
||||
int grantAccessVector = KeyPermission.USE | KeyPermission.GET_INFO | KeyPermission.DELETE;
|
||||
|
||||
try {
|
||||
key = KeyStore2.getInstance().grant(key, uid, grantAccessVector);
|
||||
} catch (android.security.KeyStoreException e) {
|
||||
Log.e(TAG, "Failed to get grant for KeyStore key.", e);
|
||||
throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
|
||||
}
|
||||
return String.format("%s%016X", APPLICATION_KEY_GRANT_PREFIX, key.nspace);
|
||||
}
|
||||
|
||||
private static boolean useKeyStore2() {
|
||||
return android.security.keystore2.AndroidKeyStoreProvider.isInstalled();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ public class LockSettingsServiceTestable extends LockSettingsService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) {
|
||||
public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
|
||||
return mRecoverableKeyStoreManager;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user