summaryrefslogtreecommitdiff
path: root/keystore/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'keystore/java/android')
-rw-r--r--keystore/java/android/security/AndroidKeyStoreMaintenance.java25
-rw-r--r--keystore/java/android/security/AppUriAuthenticationPolicy.java17
-rw-r--r--keystore/java/android/security/IKeyChainService.aidl6
-rw-r--r--keystore/java/android/security/KeyChain.java157
-rw-r--r--keystore/java/android/security/KeyStore.java4
-rw-r--r--keystore/java/android/security/KeyStore2.java45
-rw-r--r--keystore/java/android/security/LegacyVpnProfileStore.java142
-rw-r--r--keystore/java/android/security/UrisToAliases.java18
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java10
-rw-r--r--keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java6
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java10
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java37
-rw-r--r--keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java23
13 files changed, 464 insertions, 36 deletions
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index c81c8c54d88a..ed789f03f9ba 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.security.usermanager.IKeystoreUserManager;
+import android.system.keystore2.Domain;
import android.system.keystore2.ResponseCode;
import android.util.Log;
@@ -39,7 +40,7 @@ public class AndroidKeyStoreMaintenance {
}
/**
- * Informs keystore2 about adding a user
+ * Informs Keystore 2.0 about adding a user
*
* @param userId - Android user id of the user being added
* @return 0 if successful or a {@code ResponseCode}
@@ -60,7 +61,7 @@ public class AndroidKeyStoreMaintenance {
}
/**
- * Informs keystore2 about removing a usergit mer
+ * Informs Keystore 2.0 about removing a usergit mer
*
* @param userId - Android user id of the user being removed
* @return 0 if successful or a {@code ResponseCode}
@@ -81,7 +82,7 @@ public class AndroidKeyStoreMaintenance {
}
/**
- * Informs keystore2 about changing user's password
+ * Informs Keystore 2.0 about changing user's password
*
* @param userId - Android user id of the user
* @param password - a secret derived from the synthetic password provided by the
@@ -102,4 +103,22 @@ public class AndroidKeyStoreMaintenance {
return SYSTEM_ERROR;
}
}
+
+ /**
+ * Informs Keystore 2.0 that an app was uninstalled and the corresponding namspace is to
+ * be cleared.
+ */
+ public static int clearNamespace(@Domain int domain, long namespace) {
+ if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
+ try {
+ getService().clearNamespace(domain, namespace);
+ return 0;
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "clearNamespace failed", e);
+ return e.errorCode;
+ } catch (Exception e) {
+ Log.e(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ }
+ }
}
diff --git a/keystore/java/android/security/AppUriAuthenticationPolicy.java b/keystore/java/android/security/AppUriAuthenticationPolicy.java
index 0244ce97c0d4..df79912128fe 100644
--- a/keystore/java/android/security/AppUriAuthenticationPolicy.java
+++ b/keystore/java/android/security/AppUriAuthenticationPolicy.java
@@ -238,4 +238,21 @@ public final class AppUriAuthenticationPolicy implements Parcelable {
return aliases;
}
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof AppUriAuthenticationPolicy)) {
+ return false;
+ }
+ AppUriAuthenticationPolicy other = (AppUriAuthenticationPolicy) obj;
+ return Objects.equals(mAppToUris, other.mAppToUris);
+ }
+
+ @Override
+ public int hashCode() {
+ return mAppToUris.hashCode();
+ }
+
}
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index f708298a2cbd..091f5795784f 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -37,8 +37,6 @@ interface IKeyChainService {
void setUserSelectable(String alias, boolean isUserSelectable);
int generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
- int attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags,
- out KeymasterCertificateChain chain);
boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain);
// APIs used by CertInstaller and DevicePolicyManager
@@ -65,8 +63,12 @@ interface IKeyChainService {
AppUriAuthenticationPolicy getCredentialManagementAppPolicy();
String getPredefinedAliasForPackageAndUri(String packageName, in Uri uri);
void removeCredentialManagementApp();
+ boolean isCredentialManagementApp(String packageName);
// APIs used by KeyChainActivity
void setGrant(int uid, String alias, boolean value);
boolean hasGrant(int uid, String alias);
+
+ // API used by Wifi
+ String getWifiKeyGrantAsUser(String alias);
}
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 97819c56fd5a..11cb2b7c724b 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.WorkerThread;
import android.app.Activity;
@@ -44,6 +45,8 @@ import android.os.UserManager;
import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
import android.util.Log;
import com.android.org.conscrypt.TrustedCertificateStore;
@@ -421,6 +424,15 @@ public final class KeyChain {
* credentials. This is limited to unmanaged devices. The authentication policy must be
* provided to be able to make this request successfully.
*
+ * <p> This intent should be started using {@link Activity#startActivityForResult(Intent, int)}
+ * to verify whether the request was successful and whether the user accepted or denied the
+ * request. If the user successfully receives and accepts the request, the result code will be
+ * {@link Activity#RESULT_OK}, otherwise the result code will be
+ * {@link Activity#RESULT_CANCELED}.
+ *
+ * <p> {@link KeyChain#isCredentialManagementApp(Context)} should be used to determine whether
+ * an app is already the credential management app.
+ *
* @param policy The authentication policy determines which alias for a private key and
* certificate pair should be used for authentication.
*/
@@ -589,6 +601,55 @@ public final class KeyChain {
}
/**
+ * Check whether the caller is the credential management app {@link CredentialManagementApp}.
+ * The credential management app has the ability to manage the user's KeyChain credentials
+ * on unmanaged devices.
+ *
+ * <p> {@link KeyChain#createManageCredentialsIntent} should be used by an app to request to
+ * become the credential management app. The user must approve this request before the app can
+ * manage the user's credentials. There can only be one credential management on the device.
+ *
+ * @return {@code true} if the caller is the credential management app.
+ */
+ public static boolean isCredentialManagementApp(@NonNull Context context) {
+ boolean isCredentialManagementApp = false;
+ try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
+ isCredentialManagementApp = keyChainConnection.getService()
+ .isCredentialManagementApp(context.getPackageName());
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted while checking whether the caller is the "
+ + "credential management app.", e);
+ } catch (SecurityException e) {
+ isCredentialManagementApp = false;
+ }
+ return isCredentialManagementApp;
+ }
+
+ /**
+ * Called by the credential management app to get the authentication policy
+ * {@link AppUriAuthenticationPolicy}.
+ *
+ * @return the credential management app's authentication policy.
+ * @throws SecurityException if the caller is not the credential management app.
+ */
+ @NonNull
+ public static AppUriAuthenticationPolicy getCredentialManagementAppPolicy(
+ @NonNull Context context) throws SecurityException {
+ AppUriAuthenticationPolicy policy = null;
+ try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
+ policy = keyChainConnection.getService().getCredentialManagementAppPolicy();
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(
+ "Interrupted while getting credential management app policy.", e);
+ }
+ return policy;
+ }
+
+ /**
* Set a credential management app. The credential management app has the ability to manage
* the user's KeyChain credentials on unmanaged devices.
*
@@ -682,6 +743,33 @@ public final class KeyChain {
return null;
}
+ /**
+ * This prefix is used to disambiguate grant aliase strings from normal key alias strings.
+ * Technically, a key alias string can use the same prefix. However, a collision does not
+ * lead to privilege escalation, because grants are access controlled in the Keystore daemon.
+ * @hide
+ */
+ public static final String GRANT_ALIAS_PREFIX = "ks2_keychain_grant_id:";
+
+ private static KeyDescriptor getGrantDescriptor(String keyid) {
+ KeyDescriptor result = new KeyDescriptor();
+ result.domain = Domain.GRANT;
+ result.blob = null;
+ result.alias = null;
+ try {
+ result.nspace = Long.parseUnsignedLong(
+ keyid.substring(GRANT_ALIAS_PREFIX.length()), 16 /* radix */);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ return result;
+ }
+
+ /** @hide */
+ public static String getGrantString(KeyDescriptor key) {
+ return String.format(GRANT_ALIAS_PREFIX + "%016X", key.nspace);
+ }
+
/** @hide */
@Nullable @WorkerThread
public static KeyPair getKeyPair(@NonNull Context context, @NonNull String alias)
@@ -705,11 +793,23 @@ public final class KeyChain {
if (keyId == null) {
return null;
+ }
+
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ try {
+ return android.security.keystore2.AndroidKeyStoreProvider
+ .loadAndroidKeyStoreKeyPairFromKeystore(
+ KeyStore2.getInstance(),
+ getGrantDescriptor(keyId));
+ } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) {
+ throw new KeyChainException(e);
+ }
} else {
try {
return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
KeyStore.getInstance(), keyId, KeyStore.UID_SELF);
- } catch (RuntimeException | UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) {
+ } catch (RuntimeException | UnrecoverableKeyException
+ | KeyPermanentlyInvalidatedException e) {
throw new KeyChainException(e);
}
}
@@ -827,11 +927,8 @@ public final class KeyChain {
@Deprecated
public static boolean isBoundKeyAlgorithm(
@NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) {
- if (!isKeyAlgorithmSupported(algorithm)) {
- return false;
- }
-
- return KeyStore.getInstance().isHardwareBacked(algorithm);
+ // All supported algorithms are hardware backed. Individual keys may not be.
+ return true;
}
/** @hide */
@@ -917,6 +1014,54 @@ public final class KeyChain {
}
/**
+ * Returns a persistable grant string that allows WiFi stack to access the key using Keystore
+ * SSL engine.
+ *
+ * @return grant string or null if key is not granted or doesn't exist.
+ *
+ * The key should be granted to Process.WIFI_UID.
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ @WorkerThread
+ public static String getWifiKeyGrantAsUser(
+ @NonNull Context context, @NonNull UserHandle user, @NonNull String alias) {
+ try (KeyChainConnection keyChainConnection =
+ bindAsUser(context.getApplicationContext(), user)) {
+ return keyChainConnection.getService().getWifiKeyGrantAsUser(alias);
+ } catch (RemoteException | RuntimeException e) {
+ Log.i(LOG, "Couldn't get grant for wifi", e);
+ return null;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Log.i(LOG, "Interrupted while getting grant for wifi", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns whether the key is granted to WiFi stack.
+ * @hide
+ */
+ @SystemApi
+ @WorkerThread
+ public static boolean hasWifiKeyGrantAsUser(
+ @NonNull Context context, @NonNull UserHandle user, @NonNull String alias) {
+ try (KeyChainConnection keyChainConnection =
+ bindAsUser(context.getApplicationContext(), user)) {
+ return keyChainConnection.getService().hasGrant(Process.WIFI_UID, alias);
+ } catch (RemoteException | RuntimeException e) {
+ Log.i(LOG, "Couldn't query grant for wifi", e);
+ return false;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Log.i(LOG, "Interrupted while querying grant for wifi", e);
+ return false;
+ }
+ }
+
+ /**
* Bind to KeyChainService in the target user.
* Caller should call unbindService on the result when finished.
*
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 198df40c7d7b..93658e69eac8 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -43,6 +43,7 @@ import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeystoreResponse;
import android.security.keystore.UserNotAuthenticatedException;
+import android.system.keystore2.Domain;
import android.util.Log;
import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
@@ -466,6 +467,9 @@ public class KeyStore {
public boolean clearUid(int uid) {
try {
+ if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
+ return AndroidKeyStoreMaintenance.clearNamespace(Domain.APP, uid) == 0;
+ }
return mBinder.clear_uid(uid) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 476e4d7b7b18..6ac3821d0f9c 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -24,6 +24,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.security.keymaster.KeymasterDefs;
+import android.system.keystore2.Domain;
import android.system.keystore2.IKeystoreService;
import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyEntryResponse;
@@ -157,6 +158,50 @@ public class KeyStore2 {
}
/**
+ * Grant string prefix as used by the keystore boringssl engine. Must be kept in sync
+ * with system/security/keystore-engine. Note: The prefix here includes the 0x which
+ * std::stringstream used in keystore-engine needs to identify the number as hex represented.
+ * Here we include it in the prefix, because Long#parseUnsignedLong does not understand it
+ * and gets the radix as explicit argument.
+ * @hide
+ */
+ private static final String KEYSTORE_ENGINE_GRANT_ALIAS_PREFIX =
+ "ks2_keystore-engine_grant_id:0x";
+
+ /**
+ * This function turns a grant identifier into a specific string that is understood by the
+ * keystore-engine in system/security/keystore-engine. Is only used by VPN and WI-FI components
+ * to allow certain system components like racoon or vendor components like WPA supplicant
+ * to use keystore keys with boring ssl.
+ *
+ * @param grantId the grant id as returned by {@link #grant} in the {@code nspace} filed of
+ * the resulting {@code KeyDescriptor}.
+ * @return The grant descriptor string.
+ * @hide
+ */
+ public static String makeKeystoreEngineGrantString(long grantId) {
+ return String.format("%s%016X", KEYSTORE_ENGINE_GRANT_ALIAS_PREFIX, grantId);
+ }
+
+ /**
+ * Convenience function to turn a keystore engine grant string as returned by
+ * {@link #makeKeystoreEngineGrantString(long)} back into a grant KeyDescriptor.
+ *
+ * @param grantString As string returned by {@link #makeKeystoreEngineGrantString(long)}
+ * @return The grant key descriptor.
+ * @hide
+ */
+ public static KeyDescriptor keystoreEngineGrantString2KeyDescriptor(String grantString) {
+ KeyDescriptor key = new KeyDescriptor();
+ key.domain = Domain.GRANT;
+ key.nspace = Long.parseUnsignedLong(
+ grantString.substring(KEYSTORE_ENGINE_GRANT_ALIAS_PREFIX.length()), 16);
+ key.alias = null;
+ key.blob = null;
+ return key;
+ }
+
+ /**
* Create a grant that allows the grantee identified by {@code granteeUid} to use
* the key specified by {@code descriptor} withint the restrictions given by
* {@code accessVectore}.
diff --git a/keystore/java/android/security/LegacyVpnProfileStore.java b/keystore/java/android/security/LegacyVpnProfileStore.java
new file mode 100644
index 000000000000..41cfb2707fcf
--- /dev/null
+++ b/keystore/java/android/security/LegacyVpnProfileStore.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.annotation.NonNull;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.security.keystore.AndroidKeyStoreProvider;
+import android.security.vpnprofilestore.IVpnProfileStore;
+import android.util.Log;
+
+/**
+ * @hide This class allows legacy VPN access to its profiles that were stored in Keystore.
+ * The storage of unstructured blobs in Android Keystore is going away, because there is no
+ * architectural or security benefit of storing profiles in keystore over storing them
+ * in the file system. This class allows access to the blobs that still exist in keystore.
+ * And it stores new blob in a database that is still owned by Android Keystore.
+ */
+public class LegacyVpnProfileStore {
+ private static final String TAG = "LegacyVpnProfileStore";
+
+ public static final int SYSTEM_ERROR = IVpnProfileStore.ERROR_SYSTEM_ERROR;
+ public static final int PROFILE_NOT_FOUND = IVpnProfileStore.ERROR_PROFILE_NOT_FOUND;
+
+ private static final String VPN_PROFILE_STORE_SERVICE_NAME = "android.security.vpnprofilestore";
+
+ private static IVpnProfileStore getService() {
+ return IVpnProfileStore.Stub.asInterface(
+ ServiceManager.checkService(VPN_PROFILE_STORE_SERVICE_NAME));
+ }
+
+ /**
+ * Stores the profile under the alias in the profile database. Existing profiles by the
+ * same name will be replaced.
+ * @param alias The name of the profile
+ * @param profile The profile.
+ * @return true if the profile was successfully added. False otherwise.
+ * @hide
+ */
+ public static boolean put(@NonNull String alias, @NonNull byte[] profile) {
+ try {
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ getService().put(alias, profile);
+ return true;
+ } else {
+ return KeyStore.getInstance().put(
+ alias, profile, KeyStore.UID_SELF, 0);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to put vpn profile.", e);
+ return false;
+ }
+ }
+
+ /**
+ * Retrieves a profile by the name alias from the profile database.
+ * @param alias Name of the profile to retrieve.
+ * @return The unstructured blob, that is the profile that was stored using
+ * LegacyVpnProfileStore#put or with
+ * android.security.Keystore.put(Credentials.VPN + alias).
+ * Returns null if no profile was found.
+ * @hide
+ */
+ public static byte[] get(@NonNull String alias) {
+ try {
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ return getService().get(alias);
+ } else {
+ return KeyStore.getInstance().get(alias, true /* suppressKeyNotFoundWarning */);
+ }
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode != PROFILE_NOT_FOUND) {
+ Log.e(TAG, "Failed to get vpn profile.", e);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to get vpn profile.", e);
+ }
+ return null;
+ }
+
+ /**
+ * Removes a profile by the name alias from the profile database.
+ * @param alias Name of the profile to be removed.
+ * @return True if a profile was removed. False if no such profile was found.
+ * @hide
+ */
+ public static boolean remove(@NonNull String alias) {
+ try {
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ getService().remove(alias);
+ return true;
+ } else {
+ return KeyStore.getInstance().delete(alias);
+ }
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode != PROFILE_NOT_FOUND) {
+ Log.e(TAG, "Failed to remove vpn profile.", e);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to remove vpn profile.", e);
+ }
+ return false;
+ }
+
+ /**
+ * Lists the vpn profiles stored in the database.
+ * @return An array of strings representing the aliases stored in the profile database.
+ * The return value may be empty but never null.
+ * @hide
+ */
+ public static @NonNull String[] list(@NonNull String prefix) {
+ try {
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ final String[] aliases = getService().list(prefix);
+ for (int i = 0; i < aliases.length; ++i) {
+ aliases[i] = aliases[i].substring(prefix.length());
+ }
+ return aliases;
+ } else {
+ final String[] result = KeyStore.getInstance().list(prefix);
+ return result != null ? result : new String[0];
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to list vpn profiles.", e);
+ }
+ return new String[0];
+ }
+}
diff --git a/keystore/java/android/security/UrisToAliases.java b/keystore/java/android/security/UrisToAliases.java
index 65d433abe166..9a8b659f3db4 100644
--- a/keystore/java/android/security/UrisToAliases.java
+++ b/keystore/java/android/security/UrisToAliases.java
@@ -30,6 +30,7 @@ import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
/**
* The mapping from URI to alias, which determines the alias to use when the user visits a URI.
@@ -135,4 +136,21 @@ public final class UrisToAliases implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeMap(mUrisToAliases);
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof UrisToAliases)) {
+ return false;
+ }
+ UrisToAliases other = (UrisToAliases) obj;
+ return Objects.equals(mUrisToAliases, other.mUrisToAliases);
+ }
+
+ @Override
+ public int hashCode() {
+ return mUrisToAliases.hashCode();
+ }
}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index c79c12cd3343..72735a787b7f 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -279,8 +279,10 @@ import javax.security.auth.x500.X500Principal;
* }
*/
public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
- private static final X500Principal DEFAULT_CERT_SUBJECT =
+ private static final X500Principal DEFAULT_ATTESTATION_CERT_SUBJECT =
new X500Principal("CN=Android Keystore Key");
+ private static final X500Principal DEFAULT_SELF_SIGNED_CERT_SUBJECT =
+ new X500Principal("CN=Fake");
private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970
private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
@@ -366,7 +368,11 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
if (certificateSubject == null) {
- certificateSubject = DEFAULT_CERT_SUBJECT;
+ if (attestationChallenge == null) {
+ certificateSubject = DEFAULT_SELF_SIGNED_CERT_SUBJECT;
+ } else {
+ certificateSubject = DEFAULT_ATTESTATION_CERT_SUBJECT;
+ }
}
if (certificateNotBefore == null) {
certificateNotBefore = DEFAULT_CERT_NOT_BEFORE;
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index c20cf01a993e..a6e33664f2b1 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -59,7 +59,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
public void writeToParcel(Parcel out, int flags) {
out.writeString(mSpec.getKeystoreAlias());
out.writeInt(mSpec.getPurposes());
- out.writeInt(mSpec.getUid());
+ out.writeInt(mSpec.getNamespace());
out.writeInt(mSpec.getKeySize());
// Only needs to support RSAKeyGenParameterSpec and ECGenParameterSpec.
@@ -125,7 +125,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
private ParcelableKeyGenParameterSpec(Parcel in) {
final String keystoreAlias = in.readString();
final int purposes = in.readInt();
- final int uid = in.readInt();
+ final int namespace = in.readInt();
final int keySize = in.readInt();
final int keySpecType = in.readInt();
@@ -177,7 +177,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
// KeyGenParameterSpec constructor (whereas using a builder would silently drop them).
mSpec = new KeyGenParameterSpec(
keystoreAlias,
- uid,
+ namespace,
keySize,
algorithmSpec,
certificateSubject,
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index b3bfd6a3a97a..e401add9ece7 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -154,7 +154,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
private KeyGenParameterSpec mSpec;
private String mEntryAlias;
- private int mEntryUid;
+ private int mEntryNamespace;
private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm;
private int mKeymasterAlgorithm = -1;
private int mKeySizeBits;
@@ -218,7 +218,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
}
mEntryAlias = spec.getKeystoreAlias();
- mEntryUid = spec.getUid();
+ mEntryNamespace = spec.getNamespace();
mSpec = spec;
mKeymasterAlgorithm = keymasterAlgorithm;
mKeySizeBits = spec.getKeySize();
@@ -439,7 +439,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
private void resetAll() {
mEntryAlias = null;
- mEntryUid = KeyProperties.NAMESPACE_APPLICATION;
+ mEntryNamespace = KeyProperties.NAMESPACE_APPLICATION;
mJcaKeyAlgorithm = null;
mKeymasterAlgorithm = -1;
mKeymasterPurposes = null;
@@ -541,10 +541,10 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
KeyDescriptor descriptor = new KeyDescriptor();
descriptor.alias = mEntryAlias;
- descriptor.domain = mEntryUid == KeyProperties.NAMESPACE_APPLICATION
+ descriptor.domain = mEntryNamespace == KeyProperties.NAMESPACE_APPLICATION
? Domain.APP
: Domain.SELINUX;
- descriptor.nspace = mEntryUid;
+ descriptor.nspace = mEntryNamespace;
descriptor.blob = null;
boolean success = false;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index e1011155248e..d36695b9b410 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -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.
@@ -273,10 +274,10 @@ public class AndroidKeyStoreProvider extends Provider {
/** @hide **/
@NonNull
public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
- @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace)
+ @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)
throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
AndroidKeyStoreKey key =
- loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace);
+ loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor);
if (key instanceof AndroidKeyStorePublicKey) {
AndroidKeyStorePublicKey publicKey = (AndroidKeyStorePublicKey) key;
return new KeyPair(publicKey, publicKey.getPrivateKey());
@@ -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(
@@ -336,8 +350,7 @@ public class AndroidKeyStoreProvider extends Provider {
@NonNull
public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
@NonNull KeyStore2 keyStore, @NonNull String alias, int namespace)
- throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
-
+ throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
KeyDescriptor descriptor = new KeyDescriptor();
if (namespace == KeyProperties.NAMESPACE_APPLICATION) {
descriptor.nspace = KeyProperties.NAMESPACE_APPLICATION; // ignored;
@@ -348,6 +361,18 @@ public class AndroidKeyStoreProvider extends Provider {
}
descriptor.alias = alias;
descriptor.blob = null;
+
+ final AndroidKeyStoreKey key = loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor);
+ if (key instanceof AndroidKeyStorePublicKey) {
+ return ((AndroidKeyStorePublicKey) key).getPrivateKey();
+ } else {
+ return key;
+ }
+ }
+
+ private static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
+ @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)
+ throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
KeyEntryResponse response = null;
try {
response = keyStore.getKeyEntry(descriptor);
@@ -397,7 +422,7 @@ public class AndroidKeyStoreProvider extends Provider {
keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata,
new KeyStoreSecurityLevel(response.iSecurityLevel),
- keymasterAlgorithm).getPrivateKey();
+ keymasterAlgorithm);
} else {
throw new UnrecoverableKeyException("Key algorithm unknown");
}
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
index 992454285738..0006b92b1b9b 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
@@ -18,6 +18,7 @@ package android.security.keystore2;
import android.app.ActivityThread;
import android.hardware.biometrics.BiometricManager;
+import android.hardware.security.keymint.ErrorCode;
import android.security.GateKeeper;
import android.security.KeyStore;
import android.security.KeyStoreException;
@@ -183,15 +184,19 @@ abstract class KeyStoreCryptoOperationUtils {
try {
operation.abort();
} catch (KeyStoreException e) {
- // We log this error, but we can afford to ignore it. Dropping the reference
- // to the KeyStoreOperation is enough to clean up all related resources even
- // in the Keystore daemon. We log it anyway, because it may indicate some
- // underlying problem that is worth debugging.
- Log.w(
- "KeyStoreCryptoOperationUtils",
- "Encountered error trying to abort a keystore operation.",
- e
- );
+ // Invalid operation handle is very common at this point. It occurs every time
+ // an already finalized operation gets aborted.
+ if (e.getErrorCode() != ErrorCode.INVALID_OPERATION_HANDLE) {
+ // This error gets logged but ignored. Dropping the reference
+ // to the KeyStoreOperation is enough to clean up all related resources even
+ // in the Keystore daemon. It gets logged anyway, because it may indicate some
+ // underlying problem that is worth debugging.
+ Log.w(
+ "KeyStoreCryptoOperationUtils",
+ "Encountered error trying to abort a keystore operation.",
+ e
+ );
+ }
}
}
}