Merge "Add KeyPairGenerator.EC backed by Android KeyStore."
This commit is contained in:
@@ -50,10 +50,50 @@ import java.security.spec.X509EncodedKeySpec;
|
|||||||
*
|
*
|
||||||
* {@hide}
|
* {@hide}
|
||||||
*/
|
*/
|
||||||
public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
|
public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
|
||||||
|
|
||||||
|
public static class RSA extends AndroidKeyPairGenerator {
|
||||||
|
public RSA() {
|
||||||
|
super("RSA");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EC extends AndroidKeyPairGenerator {
|
||||||
|
public EC() {
|
||||||
|
super("EC");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These must be kept in sync with system/security/keystore/defaults.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* EC */
|
||||||
|
private static final int EC_DEFAULT_KEY_SIZE = 256;
|
||||||
|
private static final int EC_MIN_KEY_SIZE = 192;
|
||||||
|
private static final int EC_MAX_KEY_SIZE = 521;
|
||||||
|
|
||||||
|
/* RSA */
|
||||||
|
private static final int RSA_DEFAULT_KEY_SIZE = 2048;
|
||||||
|
private static final int RSA_MIN_KEY_SIZE = 512;
|
||||||
|
private static final int RSA_MAX_KEY_SIZE = 8192;
|
||||||
|
|
||||||
|
private final String mAlgorithm;
|
||||||
|
|
||||||
private android.security.KeyStore mKeyStore;
|
private android.security.KeyStore mKeyStore;
|
||||||
|
|
||||||
private KeyPairGeneratorSpec mSpec;
|
private KeyPairGeneratorSpec mSpec;
|
||||||
|
private String mKeyAlgorithm;
|
||||||
|
private int mKeyType;
|
||||||
|
private int mKeySize;
|
||||||
|
|
||||||
|
protected AndroidKeyPairGenerator(String algorithm) {
|
||||||
|
mAlgorithm = algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAlgorithm() {
|
||||||
|
return mAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a KeyPair which is backed by the Android keystore service. You
|
* Generate a KeyPair which is backed by the Android keystore service. You
|
||||||
@@ -88,12 +128,11 @@ public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
|
|||||||
|
|
||||||
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
|
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
|
||||||
|
|
||||||
final int keyType = KeyStore.getKeyTypeForAlgorithm(mSpec.getKeyType());
|
byte[][] args = getArgsForKeyType(mKeyType, mSpec.getAlgorithmParameterSpec());
|
||||||
byte[][] args = getArgsForKeyType(keyType, mSpec.getAlgorithmParameterSpec());
|
|
||||||
|
|
||||||
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
|
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
|
||||||
if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, keyType,
|
if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mKeyType, mKeySize,
|
||||||
mSpec.getKeySize(), mSpec.getFlags(), args)) {
|
mSpec.getFlags(), args)) {
|
||||||
throw new IllegalStateException("could not generate key in keystore");
|
throw new IllegalStateException("could not generate key in keystore");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +148,7 @@ public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
|
|||||||
|
|
||||||
final PublicKey pubKey;
|
final PublicKey pubKey;
|
||||||
try {
|
try {
|
||||||
final KeyFactory keyFact = KeyFactory.getInstance(mSpec.getKeyType());
|
final KeyFactory keyFact = KeyFactory.getInstance(mKeyAlgorithm);
|
||||||
pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
|
pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
throw new IllegalStateException("Can't instantiate key generator", e);
|
throw new IllegalStateException("Can't instantiate key generator", e);
|
||||||
@@ -117,18 +156,9 @@ public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
|
|||||||
throw new IllegalStateException("keystore returned invalid key encoding", e);
|
throw new IllegalStateException("keystore returned invalid key encoding", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
|
|
||||||
certGen.setPublicKey(pubKey);
|
|
||||||
certGen.setSerialNumber(mSpec.getSerialNumber());
|
|
||||||
certGen.setSubjectDN(mSpec.getSubjectDN());
|
|
||||||
certGen.setIssuerDN(mSpec.getSubjectDN());
|
|
||||||
certGen.setNotBefore(mSpec.getStartDate());
|
|
||||||
certGen.setNotAfter(mSpec.getEndDate());
|
|
||||||
certGen.setSignatureAlgorithm(getDefaultSignatureAlgorithmForKeyType(mSpec.getKeyType()));
|
|
||||||
|
|
||||||
final X509Certificate cert;
|
final X509Certificate cert;
|
||||||
try {
|
try {
|
||||||
cert = certGen.generate(privKey);
|
cert = generateCertificate(privKey, pubKey);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
|
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
|
||||||
throw new IllegalStateException("Can't generate certificate", e);
|
throw new IllegalStateException("Can't generate certificate", e);
|
||||||
@@ -151,13 +181,78 @@ public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
|
|||||||
return new KeyPair(pubKey, privKey);
|
return new KeyPair(pubKey, privKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getDefaultSignatureAlgorithmForKeyType(String keyType) {
|
@SuppressWarnings("deprecation")
|
||||||
if ("RSA".equalsIgnoreCase(keyType)) {
|
private X509Certificate generateCertificate(PrivateKey privateKey, PublicKey publicKey)
|
||||||
|
throws Exception {
|
||||||
|
final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
|
||||||
|
certGen.setPublicKey(publicKey);
|
||||||
|
certGen.setSerialNumber(mSpec.getSerialNumber());
|
||||||
|
certGen.setSubjectDN(mSpec.getSubjectDN());
|
||||||
|
certGen.setIssuerDN(mSpec.getSubjectDN());
|
||||||
|
certGen.setNotBefore(mSpec.getStartDate());
|
||||||
|
certGen.setNotAfter(mSpec.getEndDate());
|
||||||
|
certGen.setSignatureAlgorithm(getDefaultSignatureAlgorithmForKeyAlgorithm(mKeyAlgorithm));
|
||||||
|
return certGen.generate(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
|
||||||
|
String result = spec.getKeyType();
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return getAlgorithm();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getDefaultKeySize(int keyType) {
|
||||||
|
if (keyType == NativeCrypto.EVP_PKEY_EC) {
|
||||||
|
return EC_DEFAULT_KEY_SIZE;
|
||||||
|
} else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
|
||||||
|
return RSA_DEFAULT_KEY_SIZE;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkValidKeySize(String keyAlgorithm, int keyType, int keySize)
|
||||||
|
throws InvalidAlgorithmParameterException {
|
||||||
|
if (keyType == NativeCrypto.EVP_PKEY_EC) {
|
||||||
|
if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
|
||||||
|
throw new InvalidAlgorithmParameterException("EC keys must be >= "
|
||||||
|
+ EC_MIN_KEY_SIZE + " and <= " + EC_MAX_KEY_SIZE);
|
||||||
|
}
|
||||||
|
} else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
|
||||||
|
if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
|
||||||
|
throw new InvalidAlgorithmParameterException("RSA keys must be >= "
|
||||||
|
+ RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new InvalidAlgorithmParameterException(
|
||||||
|
"Unsupported key algorithm: " + keyAlgorithm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkCorrectParametersSpec(int keyType, int keySize,
|
||||||
|
AlgorithmParameterSpec spec) throws InvalidAlgorithmParameterException {
|
||||||
|
if (keyType == NativeCrypto.EVP_PKEY_RSA && spec != null) {
|
||||||
|
if (spec instanceof RSAKeyGenParameterSpec) {
|
||||||
|
RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
|
||||||
|
if (keySize != -1 && keySize != rsaSpec.getKeysize()) {
|
||||||
|
throw new InvalidAlgorithmParameterException("RSA key size must match: "
|
||||||
|
+ keySize + " vs " + rsaSpec.getKeysize());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new InvalidAlgorithmParameterException(
|
||||||
|
"RSA may only use RSAKeyGenParameterSpec");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getDefaultSignatureAlgorithmForKeyAlgorithm(String algorithm) {
|
||||||
|
if ("RSA".equalsIgnoreCase(algorithm)) {
|
||||||
return "sha256WithRSA";
|
return "sha256WithRSA";
|
||||||
} else if ("EC".equalsIgnoreCase(keyType)) {
|
} else if ("EC".equalsIgnoreCase(algorithm)) {
|
||||||
return "sha256WithECDSA";
|
return "sha256WithECDSA";
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Unsupported key type " + keyType);
|
throw new IllegalArgumentException("Unsupported key type " + algorithm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +285,26 @@ public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KeyPairGeneratorSpec spec = (KeyPairGeneratorSpec) params;
|
KeyPairGeneratorSpec spec = (KeyPairGeneratorSpec) params;
|
||||||
|
String keyAlgorithm = getKeyAlgorithm(spec);
|
||||||
|
int keyType = KeyStore.getKeyTypeForAlgorithm(keyAlgorithm);
|
||||||
|
if (keyType == -1) {
|
||||||
|
throw new InvalidAlgorithmParameterException(
|
||||||
|
"Unsupported key algorithm: " + keyAlgorithm);
|
||||||
|
}
|
||||||
|
int keySize = spec.getKeySize();
|
||||||
|
if (keySize == -1) {
|
||||||
|
keySize = getDefaultKeySize(keyType);
|
||||||
|
if (keySize == -1) {
|
||||||
|
throw new InvalidAlgorithmParameterException(
|
||||||
|
"Unsupported key algorithm: " + keyAlgorithm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkCorrectParametersSpec(keyType, keySize, spec.getAlgorithmParameterSpec());
|
||||||
|
checkValidKeySize(keyAlgorithm, keyType, keySize);
|
||||||
|
|
||||||
|
mKeyAlgorithm = keyAlgorithm;
|
||||||
|
mKeyType = keyType;
|
||||||
|
mKeySize = keySize;
|
||||||
mSpec = spec;
|
mSpec = spec;
|
||||||
mKeyStore = android.security.KeyStore.getInstance();
|
mKeyStore = android.security.KeyStore.getInstance();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ public class AndroidKeyStoreProvider extends Provider {
|
|||||||
put("KeyStore." + AndroidKeyStore.NAME, AndroidKeyStore.class.getName());
|
put("KeyStore." + AndroidKeyStore.NAME, AndroidKeyStore.class.getName());
|
||||||
|
|
||||||
// java.security.KeyPairGenerator
|
// java.security.KeyPairGenerator
|
||||||
put("KeyPairGenerator.RSA", AndroidKeyPairGenerator.class.getName());
|
put("KeyPairGenerator.EC", AndroidKeyPairGenerator.EC.class.getName());
|
||||||
|
put("KeyPairGenerator.RSA", AndroidKeyPairGenerator.RSA.class.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package android.security;
|
package android.security;
|
||||||
|
|
||||||
import com.android.org.conscrypt.NativeCrypto;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
@@ -26,7 +24,6 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
import java.security.spec.AlgorithmParameterSpec;
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
import java.security.spec.RSAKeyGenParameterSpec;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.security.auth.x500.X500Principal;
|
import javax.security.auth.x500.X500Principal;
|
||||||
@@ -54,19 +51,6 @@ import javax.security.auth.x500.X500Principal;
|
|||||||
* certificate signed by a real Certificate Authority.
|
* certificate signed by a real Certificate Authority.
|
||||||
*/
|
*/
|
||||||
public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
|
public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
|
||||||
/*
|
|
||||||
* These must be kept in sync with system/security/keystore/defaults.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* EC */
|
|
||||||
private static final int EC_DEFAULT_KEY_SIZE = 256;
|
|
||||||
private static final int EC_MIN_KEY_SIZE = 192;
|
|
||||||
private static final int EC_MAX_KEY_SIZE = 521;
|
|
||||||
|
|
||||||
/* RSA */
|
|
||||||
private static final int RSA_DEFAULT_KEY_SIZE = 2048;
|
|
||||||
private static final int RSA_MIN_KEY_SIZE = 512;
|
|
||||||
private static final int RSA_MAX_KEY_SIZE = 8192;
|
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
|
||||||
@@ -139,13 +123,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
throw new IllegalArgumentException("endDate < startDate");
|
throw new IllegalArgumentException("endDate < startDate");
|
||||||
}
|
}
|
||||||
|
|
||||||
final int keyTypeInt = KeyStore.getKeyTypeForAlgorithm(keyType);
|
|
||||||
if (keySize == -1) {
|
|
||||||
keySize = getDefaultKeySizeForType(keyTypeInt);
|
|
||||||
}
|
|
||||||
checkCorrectParametersSpec(keyTypeInt, keySize, spec);
|
|
||||||
checkValidKeySize(keyTypeInt, keySize);
|
|
||||||
|
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mKeystoreAlias = keyStoreAlias;
|
mKeystoreAlias = keyStoreAlias;
|
||||||
mKeyType = keyType;
|
mKeyType = keyType;
|
||||||
@@ -158,46 +135,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
mFlags = flags;
|
mFlags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getDefaultKeySizeForType(int keyType) {
|
|
||||||
if (keyType == NativeCrypto.EVP_PKEY_EC) {
|
|
||||||
return EC_DEFAULT_KEY_SIZE;
|
|
||||||
} else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
|
|
||||||
return RSA_DEFAULT_KEY_SIZE;
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Invalid key type " + keyType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkValidKeySize(int keyType, int keySize) {
|
|
||||||
if (keyType == NativeCrypto.EVP_PKEY_EC) {
|
|
||||||
if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
|
|
||||||
throw new IllegalArgumentException("EC keys must be >= " + EC_MIN_KEY_SIZE
|
|
||||||
+ " and <= " + EC_MAX_KEY_SIZE);
|
|
||||||
}
|
|
||||||
} else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
|
|
||||||
if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
|
|
||||||
throw new IllegalArgumentException("RSA keys must be >= " + RSA_MIN_KEY_SIZE
|
|
||||||
+ " and <= " + RSA_MAX_KEY_SIZE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Invalid key type " + keyType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkCorrectParametersSpec(int keyType, int keySize,
|
|
||||||
AlgorithmParameterSpec spec) {
|
|
||||||
if (keyType == NativeCrypto.EVP_PKEY_RSA && spec != null) {
|
|
||||||
if (spec instanceof RSAKeyGenParameterSpec) {
|
|
||||||
RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
|
|
||||||
if (keySize != -1 && keySize != rsaSpec.getKeysize()) {
|
|
||||||
throw new IllegalArgumentException("RSA key size must match: " + keySize
|
|
||||||
+ " vs " + rsaSpec.getKeysize());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("RSA may only use RSAKeyGenParameterSpec");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the Android context used for operations with this instance.
|
* Gets the Android context used for operations with this instance.
|
||||||
*/
|
*/
|
||||||
@@ -311,7 +248,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
|
|
||||||
private String mKeystoreAlias;
|
private String mKeystoreAlias;
|
||||||
|
|
||||||
private String mKeyType = "RSA";
|
private String mKeyType;
|
||||||
|
|
||||||
private int mKeySize = -1;
|
private int mKeySize = -1;
|
||||||
|
|
||||||
@@ -360,9 +297,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
if (keyType == null) {
|
if (keyType == null) {
|
||||||
throw new NullPointerException("keyType == null");
|
throw new NullPointerException("keyType == null");
|
||||||
} else {
|
} else {
|
||||||
try {
|
if (KeyStore.getKeyTypeForAlgorithm(keyType) == -1) {
|
||||||
KeyStore.getKeyTypeForAlgorithm(keyType);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new NoSuchAlgorithmException("Unsupported key type: " + keyType);
|
throw new NoSuchAlgorithmException("Unsupported key type: " + keyType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,13 +68,13 @@ public class KeyStore {
|
|||||||
return new KeyStore(keystore);
|
return new KeyStore(keystore);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getKeyTypeForAlgorithm(String keyType) throws IllegalArgumentException {
|
static int getKeyTypeForAlgorithm(String keyType) {
|
||||||
if ("RSA".equalsIgnoreCase(keyType)) {
|
if ("RSA".equalsIgnoreCase(keyType)) {
|
||||||
return NativeCrypto.EVP_PKEY_RSA;
|
return NativeCrypto.EVP_PKEY_RSA;
|
||||||
} else if ("EC".equalsIgnoreCase(keyType)) {
|
} else if ("EC".equalsIgnoreCase(keyType)) {
|
||||||
return NativeCrypto.EVP_PKEY_EC;
|
return NativeCrypto.EVP_PKEY_EC;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Unsupported key type: " + keyType);
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user