* Returns {@code true} if there was at least one of those types.
*/
public static boolean deleteAllTypesForAlias(KeyStore keystore, String alias) {
+ return deleteAllTypesForAlias(keystore, alias, KeyStore.UID_SELF);
+ }
+
+ /**
+ * Delete all types (private key, certificate, CA certificate) for a
+ * particular {@code alias}. All three can exist for any given alias.
+ * Returns {@code true} if there was at least one of those types.
+ */
+ public static boolean deleteAllTypesForAlias(KeyStore keystore, String alias, int uid) {
/*
* Make sure every type is deleted. There can be all three types, so
* don't use a conditional here.
*/
- return keystore.delete(Credentials.USER_PRIVATE_KEY + alias)
- | keystore.delete(Credentials.USER_SECRET_KEY + alias)
- | deleteCertificateTypesForAlias(keystore, alias);
+ return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid)
+ | keystore.delete(Credentials.USER_SECRET_KEY + alias, uid)
+ | deleteCertificateTypesForAlias(keystore, alias, uid);
}
/**
* Returns {@code true} if there was at least one of those types.
*/
public static boolean deleteCertificateTypesForAlias(KeyStore keystore, String alias) {
+ return deleteCertificateTypesForAlias(keystore, alias, KeyStore.UID_SELF);
+ }
+
+ /**
+ * Delete all types (private key, certificate, CA certificate) for a
+ * particular {@code alias}. All three can exist for any given alias.
+ * Returns {@code true} if there was at least one of those types.
+ */
+ public static boolean deleteCertificateTypesForAlias(KeyStore keystore, String alias, int uid) {
/*
* Make sure every certificate type is deleted. There can be two types,
* so don't use a conditional here.
*/
- return keystore.delete(Credentials.USER_CERTIFICATE + alias)
- | keystore.delete(Credentials.CA_CERTIFICATE + alias);
+ return keystore.delete(Credentials.USER_CERTIFICATE + alias, uid)
+ | keystore.delete(Credentials.CA_CERTIFICATE + alias, uid);
}
/**
* Returns {@code true} if an entry was was deleted.
*/
static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias) {
- return keystore.delete(Credentials.USER_PRIVATE_KEY + alias);
+ return deletePrivateKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
+ }
+
+ /**
+ * Delete private key for a particular {@code alias}.
+ * Returns {@code true} if an entry was was deleted.
+ */
+ static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
+ return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid);
}
/**
* Returns {@code true} if an entry was was deleted.
*/
public static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias) {
- return keystore.delete(Credentials.USER_SECRET_KEY + alias);
+ return deleteSecretKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
+ }
+
+ /**
+ * Delete secret key for a particular {@code alias}.
+ * Returns {@code true} if an entry was was deleted.
+ */
+ public static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
+ return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
}
}
throw new KeyChainException("keystore had a problem");
}
return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
- KeyStore.getInstance(), keyId);
+ KeyStore.getInstance(), keyId, KeyStore.UID_SELF);
} catch (RemoteException e) {
throw new KeyChainException(e);
} catch (RuntimeException e) {
* {@link KeyStoreException}.
*/
public InvalidKeyException getInvalidKeyException(
- String keystoreKeyAlias, KeyStoreException e) {
+ String keystoreKeyAlias, int uid, KeyStoreException e) {
switch (e.getErrorCode()) {
case LOCKED:
return new UserNotAuthenticatedException();
// to authenticate.
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int getKeyCharacteristicsErrorCode =
- getKeyCharacteristics(keystoreKeyAlias, null, null, keyCharacteristics);
+ getKeyCharacteristics(keystoreKeyAlias, null, null, uid,
+ keyCharacteristics);
if (getKeyCharacteristicsErrorCode != NO_ERROR) {
return new InvalidKeyException(
"Failed to obtained key characteristics",
* Returns an {@link InvalidKeyException} corresponding to the provided keystore/keymaster error
* code.
*/
- public InvalidKeyException getInvalidKeyException(String keystoreKeyAlias, int errorCode) {
- return getInvalidKeyException(keystoreKeyAlias, getKeyStoreException(errorCode));
+ public InvalidKeyException getInvalidKeyException(String keystoreKeyAlias, int uid,
+ int errorCode) {
+ return getInvalidKeyException(keystoreKeyAlias, uid, getKeyStoreException(errorCode));
}
}
purpose,
true, // permit aborting this operation if keystore runs out of resources
keymasterInputArgs,
- additionalEntropy);
+ additionalEntropy,
+ mKey.getUid());
if (opResult == null) {
throw new KeyStoreConnectException();
}
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode = getKeyStore().getKeyCharacteristics(
- key.getAlias(), null, null, keyCharacteristics);
+ key.getAlias(), null, null, key.getUid(), keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
- throw getKeyStore().getInvalidKeyException(key.getAlias(), errorCode);
+ throw getKeyStore().getInvalidKeyException(key.getAlias(), key.getUid(), errorCode);
}
long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
if (keySizeBits == -1) {
public class AndroidKeyStoreECPrivateKey extends AndroidKeyStorePrivateKey implements ECKey {
private final ECParameterSpec mParams;
- public AndroidKeyStoreECPrivateKey(String alias, ECParameterSpec params) {
- super(alias, KeyProperties.KEY_ALGORITHM_EC);
+ public AndroidKeyStoreECPrivateKey(String alias, int uid, ECParameterSpec params) {
+ super(alias, uid, KeyProperties.KEY_ALGORITHM_EC);
mParams = params;
}
private final ECParameterSpec mParams;
private final ECPoint mW;
- public AndroidKeyStoreECPublicKey(String alias, byte[] x509EncodedForm, ECParameterSpec params,
+ public AndroidKeyStoreECPublicKey(String alias, int uid, byte[] x509EncodedForm, ECParameterSpec params,
ECPoint w) {
- super(alias, KeyProperties.KEY_ALGORITHM_EC, x509EncodedForm);
+ super(alias, uid, KeyProperties.KEY_ALGORITHM_EC, x509EncodedForm);
mParams = params;
mW = w;
}
- public AndroidKeyStoreECPublicKey(String alias, ECPublicKey info) {
- this(alias, info.getEncoded(), info.getParams(), info.getW());
+ public AndroidKeyStoreECPublicKey(String alias, int uid, ECPublicKey info) {
+ this(alias, uid, info.getEncoded(), info.getParams(), info.getW());
if (!"X.509".equalsIgnoreCase(info.getFormat())) {
throw new IllegalArgumentException(
"Unsupported key export format: " + info.getFormat());
KeymasterDefs.KM_PURPOSE_SIGN,
true,
keymasterArgs,
- null); // no additional entropy needed for HMAC because it's deterministic
+ null, // no additional entropy needed for HMAC because it's deterministic
+ mKey.getUid());
if (opResult == null) {
throw new KeyStoreConnectException();
*/
public class AndroidKeyStoreKey implements Key {
private final String mAlias;
+ private final int mUid;
private final String mAlgorithm;
- public AndroidKeyStoreKey(String alias, String algorithm) {
+ public AndroidKeyStoreKey(String alias, int uid, String algorithm) {
mAlias = alias;
+ mUid = uid;
mAlgorithm = algorithm;
}
return mAlias;
}
+ int getUid() {
+ return mUid;
+ }
+
@Override
public String getAlgorithm() {
return mAlgorithm;
int result = 1;
result = prime * result + ((mAlgorithm == null) ? 0 : mAlgorithm.hashCode());
result = prime * result + ((mAlias == null) ? 0 : mAlias.hashCode());
+ result = prime * result + mUid;
return result;
}
} else if (!mAlias.equals(other.mAlias)) {
return false;
}
+ if (mUid != other.mUid) {
+ return false;
+ }
return true;
}
}
"Unsupported key type: " + key.getClass().getName()
+ ". KeyInfo can be obtained only for Android Keystore private keys");
}
- String keyAliasInKeystore = ((AndroidKeyStorePrivateKey) key).getAlias();
+ AndroidKeyStorePrivateKey keystorePrivateKey = (AndroidKeyStorePrivateKey) key;
+ String keyAliasInKeystore = keystorePrivateKey.getAlias();
String entryAlias;
if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) {
entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
}
@SuppressWarnings("unchecked")
T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo(
- mKeyStore, entryAlias, keyAliasInKeystore);
+ mKeyStore, entryAlias, keyAliasInKeystore, keystorePrivateKey.getUid());
return result;
} else if (X509EncodedKeySpec.class.equals(keySpecClass)) {
if (!(key instanceof AndroidKeyStorePublicKey)) {
KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
boolean success = false;
try {
- Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias());
+ Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias(), spec.getUid());
int errorCode = mKeyStore.generateKey(
keyAliasInKeystore,
args,
additionalEntropy,
+ spec.getUid(),
flags,
resultingKeyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
} catch (IllegalArgumentException e) {
throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
}
- SecretKey result = new AndroidKeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
+ SecretKey result = new AndroidKeyStoreSecretKey(
+ keyAliasInKeystore, spec.getUid(), keyAlgorithmJCA);
success = true;
return result;
} finally {
if (!success) {
- Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias());
+ Credentials.deleteAllTypesForAlias(
+ mKeyStore, spec.getKeystoreAlias(), spec.getUid());
}
}
}
private KeyGenParameterSpec mSpec;
private String mEntryAlias;
+ private int mEntryUid;
private boolean mEncryptionAtRestRequired;
private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm;
private int mKeymasterAlgorithm = -1;
}
mEntryAlias = spec.getKeystoreAlias();
+ mEntryUid = spec.getUid();
mSpec = spec;
mKeymasterAlgorithm = keymasterAlgorithm;
mEncryptionAtRestRequired = encryptionAtRestRequired;
private void resetAll() {
mEntryAlias = null;
+ mEntryUid = KeyStore.UID_SELF;
mJcaKeyAlgorithm = null;
mKeymasterAlgorithm = -1;
mKeymasterPurposes = null;
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + mEntryAlias;
boolean success = false;
try {
- Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
int errorCode = mKeyStore.generateKey(
privateKeyAlias,
args,
additionalEntropy,
+ mEntryUid,
flags,
resultingKeyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
KeyPair result;
try {
result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
- mKeyStore, privateKeyAlias);
+ mKeyStore, privateKeyAlias, mEntryUid);
} catch (UnrecoverableKeyException e) {
throw new ProviderException("Failed to load generated key pair from keystore", e);
}
int insertErrorCode = mKeyStore.insert(
Credentials.USER_CERTIFICATE + mEntryAlias,
certBytes,
- KeyStore.UID_SELF,
+ mEntryUid,
flags);
if (insertErrorCode != KeyStore.NO_ERROR) {
throw new ProviderException("Failed to store self-signed certificate",
return result;
} finally {
if (!success) {
- Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
}
}
}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.keystore;
+
+import java.security.KeyStore;
+import java.security.KeyStore.ProtectionParameter;
+
+class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStoreParameter {
+
+ private final int mUid;
+
+ AndroidKeyStoreLoadStoreParameter(int uid) {
+ mUid = uid;
+ }
+
+ @Override
+ public ProtectionParameter getProtectionParameter() {
+ return null;
+ }
+
+ int getUid() {
+ return mUid;
+ }
+}
*/
public class AndroidKeyStorePrivateKey extends AndroidKeyStoreKey implements PrivateKey {
- public AndroidKeyStorePrivateKey(String alias, String algorithm) {
- super(alias, algorithm);
+ public AndroidKeyStorePrivateKey(String alias, int uid, String algorithm) {
+ super(alias, uid, algorithm);
}
}
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterDefs;
+import java.io.IOException;
import java.security.KeyFactory;
import java.security.KeyPair;
+import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.ProviderException;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
import java.security.interfaces.ECKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAKey;
@NonNull
public static AndroidKeyStorePublicKey getAndroidKeyStorePublicKey(
@NonNull String alias,
+ int uid,
@NonNull @KeyProperties.KeyAlgorithmEnum String keyAlgorithm,
@NonNull byte[] x509EncodedForm) {
PublicKey publicKey;
throw new ProviderException("Invalid X.509 encoding of public key", e);
}
if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
- return new AndroidKeyStoreECPublicKey(alias, (ECPublicKey) publicKey);
+ return new AndroidKeyStoreECPublicKey(alias, uid, (ECPublicKey) publicKey);
} else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
- return new AndroidKeyStoreRSAPublicKey(alias, (RSAPublicKey) publicKey);
+ return new AndroidKeyStoreRSAPublicKey(alias, uid, (RSAPublicKey) publicKey);
} else {
throw new ProviderException("Unsupported Android Keystore public key algorithm: "
+ keyAlgorithm);
String keyAlgorithm = publicKey.getAlgorithm();
if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
return new AndroidKeyStoreECPrivateKey(
- publicKey.getAlias(), ((ECKey) publicKey).getParams());
+ publicKey.getAlias(), publicKey.getUid(), ((ECKey) publicKey).getParams());
} else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
return new AndroidKeyStoreRSAPrivateKey(
- publicKey.getAlias(), ((RSAKey) publicKey).getModulus());
+ publicKey.getAlias(), publicKey.getUid(), ((RSAKey) publicKey).getModulus());
} else {
throw new ProviderException("Unsupported Android Keystore public key algorithm: "
+ keyAlgorithm);
@NonNull
public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias)
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
throws UnrecoverableKeyException {
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode = keyStore.getKeyCharacteristics(
- privateKeyAlias, null, null, keyCharacteristics);
+ privateKeyAlias, null, null, uid, keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Failed to obtain information about private key")
.initCause(KeyStore.getKeyStoreException(errorCode));
}
ExportResult exportResult = keyStore.exportKey(
- privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null);
+ privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null, uid);
if (exportResult.resultCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Failed to obtain X.509 form of public key")
}
return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
- privateKeyAlias, jcaKeyAlgorithm, x509EncodedPublicKey);
+ privateKeyAlias, uid, jcaKeyAlgorithm, x509EncodedPublicKey);
}
@NonNull
public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias)
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
throws UnrecoverableKeyException {
AndroidKeyStorePublicKey publicKey =
- loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias);
+ loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid);
AndroidKeyStorePrivateKey privateKey =
AndroidKeyStoreProvider.getAndroidKeyStorePrivateKey(publicKey);
return new KeyPair(publicKey, privateKey);
@NonNull
public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias)
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
throws UnrecoverableKeyException {
- KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias);
+ KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid);
return (AndroidKeyStorePrivateKey) keyPair.getPrivate();
}
@NonNull
public static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String secretKeyAlias)
+ @NonNull KeyStore keyStore, @NonNull String secretKeyAlias, int uid)
throws UnrecoverableKeyException {
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode = keyStore.getKeyCharacteristics(
- secretKeyAlias, null, null, keyCharacteristics);
+ secretKeyAlias, null, null, uid, keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Failed to obtain information about key")
new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
}
- return new AndroidKeyStoreSecretKey(secretKeyAlias, keyAlgorithmString);
+ return new AndroidKeyStoreSecretKey(secretKeyAlias, uid, keyAlgorithmString);
+ }
+
+ /**
+ * Returns an {@code AndroidKeyStore} {@link java.security.KeyStore}} of the specified UID.
+ * The {@code KeyStore} contains keys and certificates owned by that UID. Such cross-UID
+ * access is permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN)
+ * all of which are system.
+ *
+ * <p>Note: the returned {@code KeyStore} is already initialized/loaded. Thus, there is
+ * no need to invoke {@code load} on it.
+ */
+ @NonNull
+ public static java.security.KeyStore getKeyStoreForUid(int uid)
+ throws KeyStoreException, NoSuchProviderException {
+ java.security.KeyStore result =
+ java.security.KeyStore.getInstance("AndroidKeyStore", PROVIDER_NAME);
+ try {
+ result.load(new AndroidKeyStoreLoadStoreParameter(uid));
+ } catch (NoSuchAlgorithmException | CertificateException | IOException e) {
+ throw new KeyStoreException(
+ "Failed to load AndroidKeyStore KeyStore for UID " + uid, e);
+ }
+ return result;
}
}
private final byte[] mEncoded;
- public AndroidKeyStorePublicKey(String alias, String algorithm, byte[] x509EncodedForm) {
- super(alias, algorithm);
+ public AndroidKeyStorePublicKey(String alias, int uid, String algorithm, byte[] x509EncodedForm) {
+ super(alias, uid, algorithm);
mEncoded = ArrayUtils.cloneIfNotEmpty(x509EncodedForm);
}
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode = getKeyStore().getKeyCharacteristics(
- keystoreKey.getAlias(), null, null, keyCharacteristics);
+ keystoreKey.getAlias(), null, null, keystoreKey.getUid(), keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
- throw getKeyStore().getInvalidKeyException(keystoreKey.getAlias(), errorCode);
+ throw getKeyStore().getInvalidKeyException(
+ keystoreKey.getAlias(), keystoreKey.getUid(), errorCode);
}
long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
if (keySizeBits == -1) {
private final BigInteger mModulus;
- public AndroidKeyStoreRSAPrivateKey(String alias, BigInteger modulus) {
- super(alias, KeyProperties.KEY_ALGORITHM_RSA);
+ public AndroidKeyStoreRSAPrivateKey(String alias, int uid, BigInteger modulus) {
+ super(alias, uid, KeyProperties.KEY_ALGORITHM_RSA);
mModulus = modulus;
}
private final BigInteger mModulus;
private final BigInteger mPublicExponent;
- public AndroidKeyStoreRSAPublicKey(String alias, byte[] x509EncodedForm, BigInteger modulus,
+ public AndroidKeyStoreRSAPublicKey(String alias, int uid, byte[] x509EncodedForm, BigInteger modulus,
BigInteger publicExponent) {
- super(alias, KeyProperties.KEY_ALGORITHM_RSA, x509EncodedForm);
+ super(alias, uid, KeyProperties.KEY_ALGORITHM_RSA, x509EncodedForm);
mModulus = modulus;
mPublicExponent = publicExponent;
}
- public AndroidKeyStoreRSAPublicKey(String alias, RSAPublicKey info) {
- this(alias, info.getEncoded(), info.getModulus(), info.getPublicExponent());
+ public AndroidKeyStoreRSAPublicKey(String alias, int uid, RSAPublicKey info) {
+ this(alias, uid, info.getEncoded(), info.getModulus(), info.getPublicExponent());
if (!"X.509".equalsIgnoreCase(info.getFormat())) {
throw new IllegalArgumentException(
"Unsupported key export format: " + info.getFormat());
*/
public class AndroidKeyStoreSecretKey extends AndroidKeyStoreKey implements SecretKey {
- public AndroidKeyStoreSecretKey(String alias, String algorithm) {
- super(alias, algorithm);
+ public AndroidKeyStoreSecretKey(String alias, int uid, String algorithm) {
+ super(alias, uid, algorithm);
}
}
if (!KeyInfo.class.equals(keySpecClass)) {
throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
}
- String keyAliasInKeystore = ((AndroidKeyStoreKey) key).getAlias();
+ AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key;
+ String keyAliasInKeystore = keystoreKey.getAlias();
String entryAlias;
if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
}
- return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore);
+ return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore, keystoreKey.getUid());
}
- static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore) {
+ static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore,
+ int keyUid) {
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
- int errorCode =
- keyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics);
+ int errorCode = keyStore.getKeyCharacteristics(
+ keyAliasInKeystore, null, null, keyUid, keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
throw new ProviderException("Failed to obtain information about key."
+ " Keystore error: " + errorCode);
mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY,
true, // permit aborting this operation if keystore runs out of resources
keymasterInputArgs,
- null // no additional entropy for begin -- only finish might need some
- );
+ null, // no additional entropy for begin -- only finish might need some
+ mKey.getUid());
if (opResult == null) {
throw new KeyStoreConnectException();
}
package android.security.keystore;
import libcore.util.EmptyArray;
-
import android.security.Credentials;
import android.security.KeyStore;
import android.security.KeyStoreParameter;
import java.io.OutputStream;
import java.security.Key;
import java.security.KeyStore.Entry;
+import java.security.KeyStore.LoadStoreParameter;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStore.ProtectionParameter;
import java.security.KeyStore.SecretKeyEntry;
public static final String NAME = "AndroidKeyStore";
private KeyStore mKeyStore;
+ private int mUid = KeyStore.UID_SELF;
@Override
public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
if (isPrivateKeyEntry(alias)) {
String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
- mKeyStore, privateKeyAlias);
+ mKeyStore, privateKeyAlias, mUid);
} else if (isSecretKeyEntry(alias)) {
String secretKeyAlias = Credentials.USER_SECRET_KEY + alias;
return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(
- mKeyStore, secretKeyAlias);
+ mKeyStore, secretKeyAlias, mUid);
} else {
// Key not found
return null;
final Certificate[] caList;
- final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+ final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
if (caBytes != null) {
final Collection<X509Certificate> caChain = toCertificates(caBytes);
throw new NullPointerException("alias == null");
}
- byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
+ byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
if (encodedCert != null) {
return getCertificateForPrivateKeyEntry(alias, encodedCert);
}
- encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+ encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
if (encodedCert != null) {
return getCertificateForTrustedCertificateEntry(encodedCert);
}
}
String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
- if (mKeyStore.contains(privateKeyAlias)) {
+ if (mKeyStore.contains(privateKeyAlias, mUid)) {
// As expected, keystore contains the private key corresponding to this public key. Wrap
// the certificate so that its getPublicKey method returns an Android Keystore
// PublicKey. This key will delegate crypto operations involving this public key to
// Android Keystore when higher-priority providers do not offer these crypto
// operations for this key.
- return wrapIntoKeyStoreCertificate(privateKeyAlias, cert);
+ return wrapIntoKeyStoreCertificate(privateKeyAlias, mUid, cert);
} else {
// This KeyStore entry/alias is supposed to contain the private key corresponding to
// the public key in this certificate, but it does not for some reason. It's probably a
* find out which key alias to use. These operations cannot work without an alias.
*/
private static KeyStoreX509Certificate wrapIntoKeyStoreCertificate(
- String privateKeyAlias, X509Certificate certificate) {
+ String privateKeyAlias, int uid, X509Certificate certificate) {
return (certificate != null)
- ? new KeyStoreX509Certificate(privateKeyAlias, certificate) : null;
+ ? new KeyStoreX509Certificate(privateKeyAlias, uid, certificate) : null;
}
private static X509Certificate toCertificate(byte[] bytes) {
}
private Date getModificationDate(String alias) {
- final long epochMillis = mKeyStore.getmtime(alias);
+ final long epochMillis = mKeyStore.getmtime(alias, mUid);
if (epochMillis == -1L) {
return null;
}
if (shouldReplacePrivateKey) {
// Delete the stored private key and any related entries before importing the
// provided key
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
int errorCode = mKeyStore.importKey(
Credentials.USER_PRIVATE_KEY + alias,
importArgs,
KeymasterDefs.KM_KEY_FORMAT_PKCS8,
pkcs8EncodedPrivateKeyBytes,
+ mUid,
flags,
resultingKeyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
}
} else {
// Keep the stored private key around -- delete all other entry types
- Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
- Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+ Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
+ Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
}
// Store the leaf certificate
int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes,
- KeyStore.UID_SELF, flags);
+ mUid, flags);
if (errorCode != KeyStore.NO_ERROR) {
throw new KeyStoreException("Failed to store certificate #0",
KeyStore.getKeyStoreException(errorCode));
// Store the certificate chain
errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes,
- KeyStore.UID_SELF, flags);
+ mUid, flags);
if (errorCode != KeyStore.NO_ERROR) {
throw new KeyStoreException("Failed to store certificate chain",
KeyStore.getKeyStoreException(errorCode));
} finally {
if (!success) {
if (shouldReplacePrivateKey) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
} else {
- Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
- Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+ Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
+ Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
}
}
}
throw new KeyStoreException(e);
}
- Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid);
String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
int errorCode = mKeyStore.importKey(
keyAliasInKeystore,
args,
KeymasterDefs.KM_KEY_FORMAT_RAW,
keyMaterial,
+ mUid,
0, // flags
new KeyCharacteristics());
if (errorCode != KeyStore.NO_ERROR) {
throw new KeyStoreException(e);
}
- if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded,
- KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
+ if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, mUid, KeyStore.FLAG_NONE)) {
throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
}
}
}
// At least one entry corresponding to this alias exists in keystore
- if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
+ if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid)) {
throw new KeyStoreException("Failed to delete entry: " + alias);
}
}
private Set<String> getUniqueAliases() {
- final String[] rawAliases = mKeyStore.list("");
+ final String[] rawAliases = mKeyStore.list("", mUid);
if (rawAliases == null) {
return new HashSet<String>();
}
throw new NullPointerException("alias == null");
}
- return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias)
- || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias)
- || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias)
- || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
+ return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid)
+ || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid)
+ || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias, mUid)
+ || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
}
@Override
throw new NullPointerException("alias == null");
}
- return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias);
+ return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid);
}
private boolean isSecretKeyEntry(String alias) {
throw new NullPointerException("alias == null");
}
- return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias);
+ return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
}
private boolean isCertificateEntry(String alias) {
throw new NullPointerException("alias == null");
}
- return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
+ return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
}
@Override
* equivalent to the USER_CERTIFICATE prefix for the Android keystore
* convention.
*/
- final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE);
+ final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE, mUid);
if (certAliases != null) {
for (String alias : certAliases) {
- final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
+ final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
if (certBytes == null) {
continue;
}
* Look at all the TrustedCertificateEntry types. Skip all the
* PrivateKeyEntry we looked at above.
*/
- final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE);
+ final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE, mUid);
if (certAliases != null) {
for (String alias : caAliases) {
if (nonCaEntries.contains(alias)) {
continue;
}
- final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+ final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
if (certBytes == null) {
continue;
}
// Unfortunate name collision.
mKeyStore = KeyStore.getInstance();
+ mUid = KeyStore.UID_SELF;
+ }
+
+ @Override
+ public void engineLoad(LoadStoreParameter param) throws IOException,
+ NoSuchAlgorithmException, CertificateException {
+ int uid = KeyStore.UID_SELF;
+ if (param != null) {
+ if (param instanceof AndroidKeyStoreLoadStoreParameter) {
+ uid = ((AndroidKeyStoreLoadStoreParameter) param).getUid();
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported param type: " + param.getClass());
+ }
+ }
+ mKeyStore = KeyStore.getInstance();
+ mUid = uid;
}
@Override
throw new KeyStoreException("entry == null");
}
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
java.security.KeyStore.TrustedCertificateEntry trE =
*/
static class KeyStoreX509Certificate extends DelegatingX509Certificate {
private final String mPrivateKeyAlias;
- KeyStoreX509Certificate(String privateKeyAlias, X509Certificate delegate) {
+ private final int mPrivateKeyUid;
+ KeyStoreX509Certificate(String privateKeyAlias, int privateKeyUid,
+ X509Certificate delegate) {
super(delegate);
mPrivateKeyAlias = privateKeyAlias;
+ mPrivateKeyUid = privateKeyUid;
}
@Override
public PublicKey getPublicKey() {
PublicKey original = super.getPublicKey();
return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
- mPrivateKeyAlias, original.getAlgorithm(), original.getEncoded());
+ mPrivateKeyAlias, mPrivateKeyUid,
+ original.getAlgorithm(), original.getEncoded());
}
}
}
import android.annotation.Nullable;
import android.app.KeyguardManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.security.KeyStore;
import android.text.TextUtils;
import java.math.BigInteger;
private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
private final String mKeystoreAlias;
+ private final int mUid;
private final int mKeySize;
private final AlgorithmParameterSpec mSpec;
private final X500Principal mCertificateSubject;
*/
public KeyGenParameterSpec(
String keyStoreAlias,
+ int uid,
int keySize,
AlgorithmParameterSpec spec,
X500Principal certificateSubject,
}
mKeystoreAlias = keyStoreAlias;
+ mUid = uid;
mKeySize = keySize;
mSpec = spec;
mCertificateSubject = certificateSubject;
}
/**
+ * Returns the UID which will own the key. {@code -1} is an alias for the UID of the current
+ * process.
+ *
+ * @hide
+ */
+ public int getUid() {
+ return mUid;
+ }
+
+ /**
* Returns the requested key size. If {@code -1}, the size should be looked up from
* {@link #getAlgorithmParameterSpec()}, if provided, otherwise an algorithm-specific default
* size should be used.
private final String mKeystoreAlias;
private @KeyProperties.PurposeEnum int mPurposes;
+ private int mUid = KeyStore.UID_SELF;
private int mKeySize = -1;
private AlgorithmParameterSpec mSpec;
private X500Principal mCertificateSubject;
}
/**
+ * Sets the UID which will own the key.
+ *
+ * @param uid UID or {@code -1} for the UID of the current process.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setUid(int uid) {
+ mUid = uid;
+ return this;
+ }
+
+ /**
* Sets the size (in bits) of the key to be generated. For instance, for RSA keys this sets
* the modulus size, for EC keys this selects a curve with a matching field size, and for
* symmetric keys this sets the size of the bitstring which is their key material.
public KeyGenParameterSpec build() {
return new KeyGenParameterSpec(
mKeystoreAlias,
+ mUid,
mKeySize,
mSpec,
mCertificateSubject,
// An error occured. However, some errors should not lead to init throwing an exception.
// See below.
InvalidKeyException e =
- keyStore.getInvalidKeyException(key.getAlias(), beginOpResultCode);
+ keyStore.getInvalidKeyException(key.getAlias(), key.getUid(), beginOpResultCode);
switch (beginOpResultCode) {
case KeyStore.OP_AUTH_NEEDED:
// Operation needs to be authorized by authenticating the user. Don't throw an
pubKey,
AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
Credentials.USER_PRIVATE_KEY + alias,
+ KeyStore.UID_SELF,
x509userCert.getPublicKey().getAlgorithm(),
x509userCert.getPublicKey().getEncoded()));
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
KeyPair keyPair = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
- keyStore, privateKeyAlias);
+ keyStore, privateKeyAlias, KeyStore.UID_SELF);
final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
certGen.setPublicKey(keyPair.getPublic());