DO NOT MERGE Support cross-UID access from AndroidKeyStore.
This is meant for exposing the pre-existing cross-UID access to keys
backed by the keystore service via higher-level JCA API. For example,
this lets system_server use Wi-Fi or VPN UID keys via JCA API.
To obtain a JCA AndroidKeyStore KeyStore for another UID, use the
hidden system API AndroidKeyStoreProvider.getKeyStoreForUid(uid).
To generate a key owned by another UID, invoke setUid(uid) on
KeyGenParameterSpec.Builder.
This CL does not change the security policy, such as which UID can
access/modify which UIDs' keys. The policy is that only certain system
UIDs are permitted to access keys of certain other system UIDs.
Cherry-picked from 3876b1be27
Bug: 23978113
Change-Id: Ie381530f41dc41c50d52f675fb9e68bc87c006de
This commit is contained in:
@@ -217,13 +217,22 @@ public class Credentials {
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,12 +241,21 @@ public class Credentials {
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,7 +263,15 @@ public class Credentials {
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,6 +279,14 @@ public class Credentials {
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,7 +374,7 @@ public final class KeyChain {
|
||||
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) {
|
||||
|
||||
@@ -640,7 +640,7 @@ public class KeyStore {
|
||||
* {@link KeyStoreException}.
|
||||
*/
|
||||
public InvalidKeyException getInvalidKeyException(
|
||||
String keystoreKeyAlias, KeyStoreException e) {
|
||||
String keystoreKeyAlias, int uid, KeyStoreException e) {
|
||||
switch (e.getErrorCode()) {
|
||||
case LOCKED:
|
||||
return new UserNotAuthenticatedException();
|
||||
@@ -658,7 +658,8 @@ public class KeyStore {
|
||||
// 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",
|
||||
@@ -708,7 +709,8 @@ public class KeyStore {
|
||||
* 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +249,8 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
|
||||
purpose,
|
||||
true, // permit aborting this operation if keystore runs out of resources
|
||||
keymasterInputArgs,
|
||||
additionalEntropy);
|
||||
additionalEntropy,
|
||||
mKey.getUid());
|
||||
if (opResult == null) {
|
||||
throw new KeyStoreConnectException();
|
||||
}
|
||||
|
||||
@@ -155,9 +155,9 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
|
||||
|
||||
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) {
|
||||
|
||||
@@ -28,8 +28,8 @@ import java.security.spec.ECParameterSpec;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,15 +30,15 @@ public class AndroidKeyStoreECPublicKey extends AndroidKeyStorePublicKey impleme
|
||||
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());
|
||||
|
||||
@@ -168,7 +168,8 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
|
||||
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();
|
||||
|
||||
@@ -25,10 +25,12 @@ import java.security.Key;
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -36,6 +38,10 @@ public class AndroidKeyStoreKey implements Key {
|
||||
return mAlias;
|
||||
}
|
||||
|
||||
int getUid() {
|
||||
return mUid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return mAlgorithm;
|
||||
@@ -59,6 +65,7 @@ public class AndroidKeyStoreKey implements Key {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -88,6 +95,9 @@ public class AndroidKeyStoreKey implements Key {
|
||||
} else if (!mAlias.equals(other.mAlias)) {
|
||||
return false;
|
||||
}
|
||||
if (mUid != other.mUid) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,8 @@ public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {
|
||||
"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());
|
||||
@@ -71,7 +72,7 @@ public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {
|
||||
}
|
||||
@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)) {
|
||||
|
||||
@@ -297,11 +297,12 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
|
||||
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) {
|
||||
@@ -315,12 +316,14 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
||||
private KeyGenParameterSpec mSpec;
|
||||
|
||||
private String mEntryAlias;
|
||||
private int mEntryUid;
|
||||
private boolean mEncryptionAtRestRequired;
|
||||
private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm;
|
||||
private int mKeymasterAlgorithm = -1;
|
||||
@@ -283,6 +284,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
||||
}
|
||||
|
||||
mEntryAlias = spec.getKeystoreAlias();
|
||||
mEntryUid = spec.getUid();
|
||||
mSpec = spec;
|
||||
mKeymasterAlgorithm = keymasterAlgorithm;
|
||||
mEncryptionAtRestRequired = encryptionAtRestRequired;
|
||||
@@ -352,6 +354,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
||||
|
||||
private void resetAll() {
|
||||
mEntryAlias = null;
|
||||
mEntryUid = KeyStore.UID_SELF;
|
||||
mJcaKeyAlgorithm = null;
|
||||
mKeymasterAlgorithm = -1;
|
||||
mKeymasterPurposes = null;
|
||||
@@ -470,12 +473,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
||||
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) {
|
||||
@@ -486,7 +490,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
||||
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);
|
||||
}
|
||||
@@ -515,7 +519,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
||||
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",
|
||||
@@ -526,7 +530,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
||||
return result;
|
||||
} finally {
|
||||
if (!success) {
|
||||
Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias);
|
||||
Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import java.security.PrivateKey;
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,15 +22,19 @@ import android.security.keymaster.ExportResult;
|
||||
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;
|
||||
@@ -168,6 +172,7 @@ public class AndroidKeyStoreProvider extends Provider {
|
||||
@NonNull
|
||||
public static AndroidKeyStorePublicKey getAndroidKeyStorePublicKey(
|
||||
@NonNull String alias,
|
||||
int uid,
|
||||
@NonNull @KeyProperties.KeyAlgorithmEnum String keyAlgorithm,
|
||||
@NonNull byte[] x509EncodedForm) {
|
||||
PublicKey publicKey;
|
||||
@@ -181,9 +186,9 @@ public class AndroidKeyStoreProvider extends Provider {
|
||||
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);
|
||||
@@ -196,10 +201,10 @@ public class AndroidKeyStoreProvider extends Provider {
|
||||
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);
|
||||
@@ -208,18 +213,18 @@ public class AndroidKeyStoreProvider extends Provider {
|
||||
|
||||
@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")
|
||||
@@ -243,15 +248,15 @@ public class AndroidKeyStoreProvider extends Provider {
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -259,19 +264,19 @@ public class AndroidKeyStoreProvider extends Provider {
|
||||
|
||||
@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")
|
||||
@@ -302,6 +307,29 @@ public class AndroidKeyStoreProvider extends Provider {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ public class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implements Publ
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -415,9 +415,10 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
|
||||
|
||||
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) {
|
||||
|
||||
@@ -29,8 +29,8 @@ public class AndroidKeyStoreRSAPrivateKey extends AndroidKeyStorePrivateKey impl
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,15 +28,15 @@ public class AndroidKeyStoreRSAPublicKey extends AndroidKeyStorePublicKey implem
|
||||
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());
|
||||
|
||||
@@ -25,7 +25,7 @@ import javax.crypto.SecretKey;
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,8 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
|
||||
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());
|
||||
@@ -67,13 +68,14 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
|
||||
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);
|
||||
|
||||
@@ -204,8 +204,8 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package android.security.keystore;
|
||||
|
||||
import libcore.util.EmptyArray;
|
||||
|
||||
import android.security.Credentials;
|
||||
import android.security.KeyStore;
|
||||
import android.security.KeyStoreParameter;
|
||||
@@ -34,6 +33,7 @@ import java.io.InputStream;
|
||||
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;
|
||||
@@ -84,6 +84,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
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,
|
||||
@@ -91,11 +92,11 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
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;
|
||||
@@ -115,7 +116,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
|
||||
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);
|
||||
|
||||
@@ -141,12 +142,12 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
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);
|
||||
}
|
||||
@@ -183,13 +184,13 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
}
|
||||
|
||||
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
|
||||
@@ -206,9 +207,9 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
* 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) {
|
||||
@@ -235,7 +236,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
}
|
||||
|
||||
private Date getModificationDate(String alias) {
|
||||
final long epochMillis = mKeyStore.getmtime(alias);
|
||||
final long epochMillis = mKeyStore.getmtime(alias, mUid);
|
||||
if (epochMillis == -1L) {
|
||||
return null;
|
||||
}
|
||||
@@ -516,13 +517,14 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
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) {
|
||||
@@ -531,13 +533,13 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
}
|
||||
} 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));
|
||||
@@ -545,7 +547,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
|
||||
// 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));
|
||||
@@ -554,10 +556,10 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -712,13 +714,14 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
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) {
|
||||
@@ -751,8 +754,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
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?");
|
||||
}
|
||||
}
|
||||
@@ -764,13 +766,13 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
}
|
||||
// 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>();
|
||||
}
|
||||
@@ -800,10 +802,10 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
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
|
||||
@@ -825,7 +827,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
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) {
|
||||
@@ -833,7 +835,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
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) {
|
||||
@@ -841,7 +843,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
throw new NullPointerException("alias == null");
|
||||
}
|
||||
|
||||
return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
|
||||
return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -876,10 +878,10 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
* 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;
|
||||
}
|
||||
@@ -896,14 +898,14 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
* 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;
|
||||
}
|
||||
@@ -936,6 +938,23 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
|
||||
// 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
|
||||
@@ -945,7 +964,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
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 =
|
||||
@@ -976,16 +995,20 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
|
||||
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;
|
||||
@@ -231,6 +232,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
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;
|
||||
@@ -254,6 +256,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
*/
|
||||
public KeyGenParameterSpec(
|
||||
String keyStoreAlias,
|
||||
int uid,
|
||||
int keySize,
|
||||
AlgorithmParameterSpec spec,
|
||||
X500Principal certificateSubject,
|
||||
@@ -293,6 +296,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
}
|
||||
|
||||
mKeystoreAlias = keyStoreAlias;
|
||||
mUid = uid;
|
||||
mKeySize = keySize;
|
||||
mSpec = spec;
|
||||
mCertificateSubject = certificateSubject;
|
||||
@@ -322,6 +326,16 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
return mKeystoreAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -531,6 +545,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
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;
|
||||
@@ -574,6 +589,19 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
mPurposes = purposes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -936,6 +964,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
public KeyGenParameterSpec build() {
|
||||
return new KeyGenParameterSpec(
|
||||
mKeystoreAlias,
|
||||
mUid,
|
||||
mKeySize,
|
||||
mSpec,
|
||||
mCertificateSubject,
|
||||
|
||||
@@ -51,7 +51,7 @@ abstract class KeyStoreCryptoOperationUtils {
|
||||
// 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
|
||||
|
||||
@@ -384,6 +384,7 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
|
||||
pubKey,
|
||||
AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
|
||||
Credentials.USER_PRIVATE_KEY + alias,
|
||||
KeyStore.UID_SELF,
|
||||
x509userCert.getPublicKey().getAlgorithm(),
|
||||
x509userCert.getPublicKey().getEncoded()));
|
||||
|
||||
|
||||
@@ -1918,7 +1918,7 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
|
||||
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());
|
||||
|
||||
Reference in New Issue
Block a user