Merge "Android Keystore keys are no longer backed by Conscrypt." into mnc-dev

This commit is contained in:
Alex Klyubin
2015-06-11 17:40:07 +00:00
committed by Android (Google) Code Review
15 changed files with 675 additions and 202 deletions

View File

@@ -29,15 +29,14 @@ import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.security.keystore.KeyInfo;
import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyProperties;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
@@ -47,7 +46,6 @@ import java.util.Locale;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import com.android.org.conscrypt.OpenSSLEngine;
import com.android.org.conscrypt.TrustedCertificateStore;
/**
@@ -90,8 +88,6 @@ import com.android.org.conscrypt.TrustedCertificateStore;
// TODO reference intent for credential installation when public
public final class KeyChain {
private static final String TAG = "KeyChain";
/**
* @hide Also used by KeyChainService implementation
*/
@@ -372,15 +368,14 @@ public final class KeyChain {
if (keyId == null) {
throw new KeyChainException("keystore had a problem");
}
final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
return engine.getPrivateKeyById(keyId);
return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
KeyStore.getInstance(), keyId);
} catch (RemoteException e) {
throw new KeyChainException(e);
} catch (RuntimeException e) {
// only certain RuntimeExceptions can be propagated across the IKeyChainService call
throw new KeyChainException(e);
} catch (InvalidKeyException e) {
} catch (UnrecoverableKeyException e) {
throw new KeyChainException(e);
} finally {
keyChainConnection.close();

View File

@@ -331,7 +331,9 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
if (keyType == null) {
throw new NullPointerException("keyType == null");
} else {
if (KeyStore.getKeyTypeForAlgorithm(keyType) == -1) {
try {
KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(keyType);
} catch (IllegalArgumentException e) {
throw new NoSuchAlgorithmException("Unsupported key type: " + keyType);
}
}

View File

@@ -19,7 +19,6 @@ package android.security;
import android.app.ActivityThread;
import android.app.Application;
import android.app.KeyguardManager;
import com.android.org.conscrypt.NativeConstants;
import android.content.Context;
import android.hardware.fingerprint.FingerprintManager;
@@ -38,7 +37,6 @@ import android.security.keymaster.OperationResult;
import android.security.keystore.KeyExpiredException;
import android.security.keystore.KeyNotYetValidException;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.security.keystore.UserNotAuthenticatedException;
import android.util.Log;
@@ -136,16 +134,6 @@ public class KeyStore {
return mToken;
}
public static int getKeyTypeForAlgorithm(@KeyProperties.KeyAlgorithmEnum String keyType) {
if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyType)) {
return NativeConstants.EVP_PKEY_RSA;
} else if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyType)) {
return NativeConstants.EVP_PKEY_EC;
} else {
return -1;
}
}
public State state(int userId) {
final int ret;
try {

View File

@@ -0,0 +1,40 @@
/*
* 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.PrivateKey;
import java.security.interfaces.ECKey;
import java.security.spec.ECParameterSpec;
/**
* EC private key (instance of {@link PrivateKey} and {@link ECKey}) backed by keystore.
*
* @hide
*/
public class AndroidKeyStoreECPrivateKey extends AndroidKeyStorePrivateKey implements ECKey {
private final ECParameterSpec mParams;
public AndroidKeyStoreECPrivateKey(String alias, ECParameterSpec params) {
super(alias, KeyProperties.KEY_ALGORITHM_EC);
mParams = params;
}
@Override
public ECParameterSpec getParams() {
return mParams;
}
}

View File

@@ -52,4 +52,42 @@ public class AndroidKeyStoreKey implements Key {
// This key does not export its key material
return null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((mAlgorithm == null) ? 0 : mAlgorithm.hashCode());
result = prime * result + ((mAlias == null) ? 0 : mAlias.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
AndroidKeyStoreKey other = (AndroidKeyStoreKey) obj;
if (mAlgorithm == null) {
if (other.mAlgorithm != null) {
return false;
}
} else if (!mAlgorithm.equals(other.mAlgorithm)) {
return false;
}
if (mAlias == null) {
if (other.mAlias != null) {
return false;
}
} else if (!mAlias.equals(other.mAlias)) {
return false;
}
return true;
}
}

View File

@@ -20,7 +20,6 @@ import android.annotation.Nullable;
import android.security.Credentials;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore;
import android.security.keymaster.ExportResult;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
@@ -44,29 +43,24 @@ import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import com.android.org.bouncycastle.jce.X509Principal;
import com.android.org.bouncycastle.jce.provider.X509CertificateObject;
import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
import com.android.org.conscrypt.OpenSSLEngine;
import libcore.util.EmptyArray;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyPairGeneratorSpi;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.ProviderException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
@@ -147,6 +141,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
SUPPORTED_EC_NIST_CURVE_NAMES.addAll(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.keySet());
Collections.sort(SUPPORTED_EC_NIST_CURVE_NAMES);
}
private final int mOriginalKeymasterAlgorithm;
private KeyStore mKeyStore;
@@ -431,24 +426,6 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings);
args.addInts(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
// TODO: Remove the digest and padding NONE workaround below once Android Keystore returns
// keys which are backed by AndroidKeyStoreBCWorkaround provider instead of Conscrypt. The
// workaround is needed because Conscrypt (via keystore-engine) uses old KeyStore API which
// translates into digest NONE and padding NONE in the new API. keystore-engine cannot be
// updated to pass in the correct padding and digest values because it uses
// OpenSSL/BoringSSL engine which performs digesting and padding prior before invoking
// KeyStore API.
if (!com.android.internal.util.ArrayUtils.contains(
mKeymasterDigests, KeymasterDefs.KM_DIGEST_NONE)) {
args.addInt(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
}
if ((!com.android.internal.util.ArrayUtils.contains(
mKeymasterSignaturePaddings, KeymasterDefs.KM_PAD_NONE))
&& (!com.android.internal.util.ArrayUtils.contains(
mKeymasterEncryptionPaddings, KeymasterDefs.KM_PAD_NONE))) {
args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
}
KeymasterUtils.addUserAuthArgs(args,
mSpec.isUserAuthenticationRequired(),
mSpec.getUserAuthenticationValidityDurationSeconds());
@@ -483,40 +460,23 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
"Failed to generate key pair", KeyStore.getKeyStoreException(errorCode));
}
final PrivateKey privKey;
final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
KeyPair result;
try {
privKey = engine.getPrivateKeyById(privateKeyAlias);
} catch (InvalidKeyException e) {
throw new ProviderException("Failed to obtain generated private key", e);
result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
mKeyStore, privateKeyAlias);
} catch (UnrecoverableKeyException e) {
throw new ProviderException("Failed to load generated key pair from keystore", e);
}
ExportResult exportResult =
mKeyStore.exportKey(
privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null);
if (exportResult == null) {
throw new KeyStoreConnectException();
} else if (exportResult.resultCode != KeyStore.NO_ERROR) {
if (!mJcaKeyAlgorithm.equalsIgnoreCase(result.getPrivate().getAlgorithm())) {
throw new ProviderException(
"Failed to obtain X.509 form of generated public key",
KeyStore.getKeyStoreException(exportResult.resultCode));
}
final byte[] pubKeyBytes = exportResult.exportData;
final PublicKey pubKey;
try {
final KeyFactory keyFact = KeyFactory.getInstance(mJcaKeyAlgorithm);
pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
} catch (NoSuchAlgorithmException e) {
throw new ProviderException(
"Failed to obtain " + mJcaKeyAlgorithm + " KeyFactory", e);
} catch (InvalidKeySpecException e) {
throw new ProviderException("Invalid X.509 encoding of generated public key", e);
"Generated key pair algorithm does not match requested algorithm: "
+ result.getPrivate().getAlgorithm() + " vs " + mJcaKeyAlgorithm);
}
final X509Certificate cert;
try {
cert = generateSelfSignedCertificate(privKey, pubKey);
cert = generateSelfSignedCertificate(result.getPrivate(), result.getPublic());
} catch (Exception e) {
throw new ProviderException("Failed to generate self-signed certificate", e);
}
@@ -539,7 +499,6 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
KeyStore.getKeyStoreException(insertErrorCode));
}
KeyPair result = new KeyPair(pubKey, privKey);
success = true;
return result;
} finally {

View File

@@ -16,11 +16,28 @@
package android.security.keystore;
import android.annotation.NonNull;
import android.security.KeyStore;
import android.security.keymaster.ExportResult;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterDefs;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
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.interfaces.ECKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.Mac;
@@ -146,4 +163,145 @@ public class AndroidKeyStoreProvider extends Provider {
}
return ((KeyStoreCryptoOperation) spi).getOperationHandle();
}
@NonNull
public static AndroidKeyStorePublicKey getAndroidKeyStorePublicKey(
@NonNull String alias,
@NonNull @KeyProperties.KeyAlgorithmEnum String keyAlgorithm,
@NonNull byte[] x509EncodedForm) {
PublicKey publicKey;
try {
KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedForm));
} catch (NoSuchAlgorithmException e) {
throw new ProviderException(
"Failed to obtain " + keyAlgorithm + " KeyFactory", e);
} catch (InvalidKeySpecException e) {
throw new ProviderException("Invalid X.509 encoding of public key", e);
}
if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
return new AndroidKeyStoreECPublicKey(alias, (ECPublicKey) publicKey);
} else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
return new AndroidKeyStoreRSAPublicKey(alias, (RSAPublicKey) publicKey);
} else {
throw new ProviderException("Unsupported Android Keystore public key algorithm: "
+ keyAlgorithm);
}
}
@NonNull
public static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey(
@NonNull AndroidKeyStorePublicKey publicKey) {
String keyAlgorithm = publicKey.getAlgorithm();
if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
return new AndroidKeyStoreECPrivateKey(
publicKey.getAlias(), ((ECKey) publicKey).getParams());
} else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
return new AndroidKeyStoreRSAPrivateKey(
publicKey.getAlias(), ((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)
throws UnrecoverableKeyException {
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode = keyStore.getKeyCharacteristics(
privateKeyAlias, null, null, 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);
if (exportResult.resultCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Failed to obtain X.509 form of public key")
.initCause(KeyStore.getKeyStoreException(errorCode));
}
final byte[] x509EncodedPublicKey = exportResult.exportData;
int keymasterAlgorithm = keyCharacteristics.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
if (keymasterAlgorithm == -1) {
throw new UnrecoverableKeyException("Key algorithm unknown");
}
String jcaKeyAlgorithm;
try {
jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(
keymasterAlgorithm);
} catch (IllegalArgumentException e) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Failed to load private key")
.initCause(e);
}
return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
privateKeyAlias, jcaKeyAlgorithm, x509EncodedPublicKey);
}
@NonNull
public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
@NonNull KeyStore keyStore, @NonNull String privateKeyAlias)
throws UnrecoverableKeyException {
AndroidKeyStorePublicKey publicKey =
loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias);
AndroidKeyStorePrivateKey privateKey =
AndroidKeyStoreProvider.getAndroidKeyStorePrivateKey(publicKey);
return new KeyPair(publicKey, privateKey);
}
@NonNull
public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
@NonNull KeyStore keyStore, @NonNull String privateKeyAlias)
throws UnrecoverableKeyException {
KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias);
return (AndroidKeyStorePrivateKey) keyPair.getPrivate();
}
@NonNull
public static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
@NonNull KeyStore keyStore, @NonNull String secretKeyAlias)
throws UnrecoverableKeyException {
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode = keyStore.getKeyCharacteristics(
secretKeyAlias, null, null, keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Failed to obtain information about key")
.initCause(KeyStore.getKeyStoreException(errorCode));
}
int keymasterAlgorithm = keyCharacteristics.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
if (keymasterAlgorithm == -1) {
throw new UnrecoverableKeyException("Key algorithm unknown");
}
List<Integer> keymasterDigests =
keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST);
int keymasterDigest;
if (keymasterDigests.isEmpty()) {
keymasterDigest = -1;
} else {
// More than one digest can be permitted for this key. Use the first one to form the
// JCA key algorithm name.
keymasterDigest = keymasterDigests.get(0);
}
@KeyProperties.KeyAlgorithmEnum String keyAlgorithmString;
try {
keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
keymasterAlgorithm, keymasterDigest);
} catch (IllegalArgumentException e) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
}
return new AndroidKeyStoreSecretKey(secretKeyAlias, keyAlgorithmString);
}
}

View File

@@ -17,6 +17,7 @@
package android.security.keystore;
import java.security.PublicKey;
import java.util.Arrays;
/**
* {@link PublicKey} backed by Android Keystore.
@@ -41,4 +42,30 @@ public class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implements Publ
public byte[] getEncoded() {
return ArrayUtils.cloneIfNotEmpty(mEncoded);
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + Arrays.hashCode(mEncoded);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
AndroidKeyStorePublicKey other = (AndroidKeyStorePublicKey) obj;
if (!Arrays.equals(mEncoded, other.mEncoded)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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.math.BigInteger;
import java.security.PrivateKey;
import java.security.interfaces.RSAKey;
/**
* RSA private key (instance of {@link PrivateKey} and {@link RSAKey}) backed by keystore.
*
* @hide
*/
public class AndroidKeyStoreRSAPrivateKey extends AndroidKeyStorePrivateKey implements RSAKey {
private final BigInteger mModulus;
public AndroidKeyStoreRSAPrivateKey(String alias, BigInteger modulus) {
super(alias, KeyProperties.KEY_ALGORITHM_RSA);
mModulus = modulus;
}
@Override
public BigInteger getModulus() {
return mModulus;
}
}

View File

@@ -25,7 +25,7 @@ import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.OperationResult;
import com.android.org.conscrypt.util.EmptyArray;
import libcore.util.EmptyArray;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;

View File

@@ -16,9 +16,6 @@
package android.security.keystore;
import com.android.org.conscrypt.OpenSSLEngine;
import com.android.org.conscrypt.OpenSSLKeyHolder;
import libcore.util.EmptyArray;
import android.security.Credentials;
@@ -35,7 +32,6 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore.Entry;
import java.security.KeyStore.PrivateKeyEntry;
@@ -45,6 +41,7 @@ import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
@@ -59,7 +56,6 @@ import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.crypto.SecretKey;
@@ -92,59 +88,17 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
UnrecoverableKeyException {
if (isPrivateKeyEntry(alias)) {
final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
try {
return engine.getPrivateKeyById(Credentials.USER_PRIVATE_KEY + alias);
} catch (InvalidKeyException e) {
UnrecoverableKeyException t = new UnrecoverableKeyException("Can't get key");
t.initCause(e);
throw t;
}
String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
mKeyStore, privateKeyAlias);
} else if (isSecretKeyEntry(alias)) {
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
String keyAliasInKeystore = Credentials.USER_SECRET_KEY + alias;
int errorCode = mKeyStore.getKeyCharacteristics(
keyAliasInKeystore, null, null, keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Failed to load information about key")
.initCause(mKeyStore.getInvalidKeyException(alias, errorCode));
}
int keymasterAlgorithm =
keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
if (keymasterAlgorithm == -1) {
keymasterAlgorithm =
keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
}
if (keymasterAlgorithm == -1) {
throw new UnrecoverableKeyException("Key algorithm unknown");
}
List<Integer> keymasterDigests =
keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST);
int keymasterDigest;
if (keymasterDigests.isEmpty()) {
keymasterDigest = -1;
} else {
// More than one digest can be permitted for this key. Use the first one to form the
// JCA key algorithm name.
keymasterDigest = keymasterDigests.get(0);
}
@KeyProperties.KeyAlgorithmEnum String keyAlgorithmString;
try {
keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
keymasterAlgorithm, keymasterDigest);
} catch (IllegalArgumentException e) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
}
return new AndroidKeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmString);
String secretKeyAlias = Credentials.USER_SECRET_KEY + alias;
return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(
mKeyStore, secretKeyAlias);
} else {
// Key not found
return null;
}
return null;
}
@Override
@@ -188,22 +142,36 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
byte[] certificate = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
if (certificate != null) {
return toCertificate(certificate);
return wrapIntoKeyStoreCertificate(
Credentials.USER_PRIVATE_KEY + alias, toCertificate(certificate));
}
certificate = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
if (certificate != null) {
return toCertificate(certificate);
return wrapIntoKeyStoreCertificate(
Credentials.USER_PRIVATE_KEY + alias, toCertificate(certificate));
}
return null;
}
/**
* Wraps the provided cerificate into {@link KeyStoreX509Certificate} so that the public key
* returned by the certificate contains information about the alias of the private key in
* keystore. This is needed so that Android Keystore crypto operations using public keys can
* find out which key alias to use. These operations cannot work without an alias.
*/
private static KeyStoreX509Certificate wrapIntoKeyStoreCertificate(
String privateKeyAlias, X509Certificate certificate) {
return (certificate != null)
? new KeyStoreX509Certificate(privateKeyAlias, certificate) : null;
}
private static X509Certificate toCertificate(byte[] bytes) {
try {
final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
return (X509Certificate) certFactory
.generateCertificate(new ByteArrayInputStream(bytes));
return (X509Certificate) certFactory.generateCertificate(
new ByteArrayInputStream(bytes));
} catch (CertificateException e) {
Log.w(NAME, "Couldn't parse certificate in keystore", e);
return null;
@@ -214,8 +182,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
private static Collection<X509Certificate> toCertificates(byte[] bytes) {
try {
final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
return (Collection<X509Certificate>) certFactory
.generateCertificates(new ByteArrayInputStream(bytes));
return (Collection<X509Certificate>) certFactory.generateCertificates(
new ByteArrayInputStream(bytes));
} catch (CertificateException e) {
Log.w(NAME, "Couldn't parse certificates in keystore", e);
return new ArrayList<X509Certificate>();
@@ -406,9 +374,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
}
final String pkeyAlias;
if (key instanceof OpenSSLKeyHolder) {
pkeyAlias = ((OpenSSLKeyHolder) key).getOpenSSLKey().getAlias();
} else if (key instanceof AndroidKeyStorePrivateKey) {
if (key instanceof AndroidKeyStorePrivateKey) {
pkeyAlias = ((AndroidKeyStoreKey) key).getAlias();
} else {
pkeyAlias = null;
@@ -851,6 +817,19 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
if (cert == null) {
return null;
}
if (!"X.509".equalsIgnoreCase(cert.getType())) {
// Only X.509 certificates supported
return null;
}
byte[] targetCertBytes;
try {
targetCertBytes = cert.getEncoded();
} catch (CertificateEncodingException e) {
return null;
}
if (targetCertBytes == null) {
return null;
}
final Set<String> nonCaEntries = new HashSet<String>();
@@ -868,10 +847,9 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
continue;
}
final Certificate c = toCertificate(certBytes);
nonCaEntries.add(alias);
if (cert.equals(c)) {
if (Arrays.equals(certBytes, targetCertBytes)) {
return alias;
}
}
@@ -893,9 +871,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
continue;
}
final Certificate c =
toCertificate(mKeyStore.get(Credentials.CA_CERTIFICATE + alias));
if (cert.equals(c)) {
if (Arrays.equals(certBytes, targetCertBytes)) {
return alias;
}
}
@@ -954,4 +930,25 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
}
}
/**
* {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from
* {@link #getPublicKey()}. This is so that crypto operations on these public keys contain
* can find out which keystore private key entry to use. This is needed so that Android Keystore
* crypto operations using public keys can find out which key alias to use. These operations
* require an alias.
*/
static class KeyStoreX509Certificate extends DelegatingX509Certificate {
private final String mPrivateKeyAlias;
KeyStoreX509Certificate(String privateKeyAlias, X509Certificate delegate) {
super(delegate);
mPrivateKeyAlias = privateKeyAlias;
}
@Override
public PublicKey getPublicKey() {
PublicKey original = super.getPublicKey();
return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
mPrivateKeyAlias, original.getAlgorithm(), original.getEncoded());
}
}
}

View File

@@ -0,0 +1,212 @@
/*
* 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.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Principal;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
class DelegatingX509Certificate extends X509Certificate {
private final X509Certificate mDelegate;
DelegatingX509Certificate(X509Certificate delegate) {
mDelegate = delegate;
}
@Override
public Set<String> getCriticalExtensionOIDs() {
return mDelegate.getCriticalExtensionOIDs();
}
@Override
public byte[] getExtensionValue(String oid) {
return mDelegate.getExtensionValue(oid);
}
@Override
public Set<String> getNonCriticalExtensionOIDs() {
return mDelegate.getNonCriticalExtensionOIDs();
}
@Override
public boolean hasUnsupportedCriticalExtension() {
return mDelegate.hasUnsupportedCriticalExtension();
}
@Override
public void checkValidity() throws CertificateExpiredException,
CertificateNotYetValidException {
mDelegate.checkValidity();
}
@Override
public void checkValidity(Date date) throws CertificateExpiredException,
CertificateNotYetValidException {
mDelegate.checkValidity(date);
}
@Override
public int getBasicConstraints() {
return mDelegate.getBasicConstraints();
}
@Override
public Principal getIssuerDN() {
return mDelegate.getIssuerDN();
}
@Override
public boolean[] getIssuerUniqueID() {
return mDelegate.getIssuerUniqueID();
}
@Override
public boolean[] getKeyUsage() {
return mDelegate.getKeyUsage();
}
@Override
public Date getNotAfter() {
return mDelegate.getNotAfter();
}
@Override
public Date getNotBefore() {
return mDelegate.getNotBefore();
}
@Override
public BigInteger getSerialNumber() {
return mDelegate.getSerialNumber();
}
@Override
public String getSigAlgName() {
return mDelegate.getSigAlgName();
}
@Override
public String getSigAlgOID() {
return mDelegate.getSigAlgOID();
}
@Override
public byte[] getSigAlgParams() {
return mDelegate.getSigAlgParams();
}
@Override
public byte[] getSignature() {
return mDelegate.getSignature();
}
@Override
public Principal getSubjectDN() {
return mDelegate.getSubjectDN();
}
@Override
public boolean[] getSubjectUniqueID() {
return mDelegate.getSubjectUniqueID();
}
@Override
public byte[] getTBSCertificate() throws CertificateEncodingException {
return mDelegate.getTBSCertificate();
}
@Override
public int getVersion() {
return mDelegate.getVersion();
}
@Override
public byte[] getEncoded() throws CertificateEncodingException {
return mDelegate.getEncoded();
}
@Override
public PublicKey getPublicKey() {
return mDelegate.getPublicKey();
}
@Override
public String toString() {
return mDelegate.toString();
}
@Override
public void verify(PublicKey key)
throws CertificateException,
NoSuchAlgorithmException,
InvalidKeyException,
NoSuchProviderException,
SignatureException {
mDelegate.verify(key);
}
@Override
public void verify(PublicKey key, String sigProvider)
throws CertificateException,
NoSuchAlgorithmException,
InvalidKeyException,
NoSuchProviderException,
SignatureException {
mDelegate.verify(key, sigProvider);
}
@Override
public List<String> getExtendedKeyUsage() throws CertificateParsingException {
return mDelegate.getExtendedKeyUsage();
}
@Override
public Collection<List<?>> getIssuerAlternativeNames() throws CertificateParsingException {
return mDelegate.getIssuerAlternativeNames();
}
@Override
public X500Principal getIssuerX500Principal() {
return mDelegate.getIssuerX500Principal();
}
@Override
public Collection<List<?>> getSubjectAlternativeNames() throws CertificateParsingException {
return mDelegate.getSubjectAlternativeNames();
}
@Override
public X500Principal getSubjectX500Principal() {
return mDelegate.getSubjectX500Principal();
}
}

View File

@@ -20,7 +20,6 @@ import android.app.Activity;
import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
import android.os.ServiceManager;
import android.security.keymaster.ExportResult;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
@@ -34,13 +33,9 @@ import android.test.suitebuilder.annotation.MediumTest;
import com.android.org.conscrypt.NativeConstants;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.security.spec.RSAKeyGenParameterSpec;
import android.util.Log;
import android.util.Base64;
/**
* Junit / Instrumentation test case for KeyStore class
*

View File

@@ -26,17 +26,21 @@ import android.test.AndroidTestCase;
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import javax.security.auth.x500.X500Principal;
@@ -158,6 +162,26 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
}
public void testKeyPairGenerator_GenerateKeyPair_EC_Unencrypted_Success() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
generator.initialize(new KeyGenParameterSpec.Builder(
TEST_ALIAS_1,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setCertificateSubject(TEST_DN_1)
.setCertificateSerialNumber(TEST_SERIAL_1)
.setCertificateNotBefore(NOW)
.setCertificateNotAfter(NOW_PLUS_10_YEARS)
.setDigests(KeyProperties.DIGEST_SHA256)
.build());
final KeyPair pair = generator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair);
assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 256, null, TEST_DN_1, TEST_SERIAL_1, NOW,
NOW_PLUS_10_YEARS);
}
public void testKeyPairGenerator_Legacy_GenerateKeyPair_EC_Unencrypted_Success()
throws Exception {
mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_1)
.setKeyType("EC")
@@ -328,19 +352,40 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
assertNotNull("The PrivateKey for the KeyPair should be not null", privKey);
assertEquals(keyType, privKey.getAlgorithm());
if ("EC".equalsIgnoreCase(keyType)) {
assertTrue("EC private key must be instanceof ECKey: " + privKey.getClass().getName(),
privKey instanceof ECKey);
assertEquals("Private and public key must have the same EC parameters",
((ECKey) pubKey).getParams(), ((ECKey) privKey).getParams());
} else if ("RSA".equalsIgnoreCase(keyType)) {
assertTrue("RSA private key must be instance of RSAKey: "
+ privKey.getClass().getName(),
privKey instanceof RSAKey);
assertEquals("Private and public key must have the same RSA modulus",
((RSAKey) pubKey).getModulus(), ((RSAKey) privKey).getModulus());
}
final byte[] userCertBytes = mAndroidKeyStore.get(Credentials.USER_CERTIFICATE + alias);
assertNotNull("The user certificate should exist for the generated entry", userCertBytes);
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
final Certificate userCert = cf
.generateCertificate(new ByteArrayInputStream(userCertBytes));
final Certificate userCert =
cf.generateCertificate(new ByteArrayInputStream(userCertBytes));
assertTrue("Certificate should be in X.509 format", userCert instanceof X509Certificate);
final X509Certificate x509userCert = (X509Certificate) userCert;
assertEquals(
"Public key used to sign certificate should have the same algorithm as in KeyPair",
pubKey.getAlgorithm(), x509userCert.getPublicKey().getAlgorithm());
assertEquals("PublicKey used to sign certificate should match one returned in KeyPair",
pubKey, x509userCert.getPublicKey());
pubKey,
AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
Credentials.USER_PRIVATE_KEY + alias,
x509userCert.getPublicKey().getAlgorithm(),
x509userCert.getPublicKey().getEncoded()));
assertEquals("The Subject DN should be the one passed into the params", dn,
x509userCert.getSubjectDN());
@@ -357,7 +402,10 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
assertDateEquals("The notAfter date should be the one passed into the params", end,
x509userCert.getNotAfter());
// Assert that the cert's signature verifies using the public key from generated KeyPair
x509userCert.verify(pubKey);
// Assert that the cert's signature verifies using the public key from the cert itself.
x509userCert.verify(x509userCert.getPublicKey());
final byte[] caCerts = mAndroidKeyStore.get(Credentials.CA_CERTIFICATE + alias);
assertNull("A list of CA certificates should not exist for the generated entry", caCerts);
@@ -368,6 +416,8 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
final byte[] pubKeyBytes = exportResult.exportData;
assertNotNull("The keystore should return the public key for the generated key",
pubKeyBytes);
assertTrue("Public key X.509 format should be as expected",
Arrays.equals(pubKey.getEncoded(), pubKeyBytes));
}
private static void assertDateEquals(String message, Date date1, Date date2) throws Exception {

View File

@@ -19,38 +19,31 @@ package android.security.keystore;
import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
import com.android.org.conscrypt.NativeConstants;
import com.android.org.conscrypt.OpenSSLEngine;
import android.security.Credentials;
import android.security.KeyStore;
import android.security.KeyStoreParameter;
import android.security.keymaster.ExportResult;
import android.security.keymaster.KeymasterDefs;
import android.test.AndroidTestCase;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyStore.Entry;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStore.TrustedCertificateEntry;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.interfaces.ECKey;
import java.security.interfaces.RSAKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
@@ -1203,14 +1196,14 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, PrivateKey expectedKey,
Certificate expectedCert, Collection<Certificate> expectedChain) throws Exception {
if (expectedKey instanceof ECPrivateKey) {
if (expectedKey instanceof ECKey) {
assertEquals("Returned PrivateKey should be what we inserted",
((ECPrivateKey) expectedKey).getParams().getCurve(),
((ECPublicKey) keyEntry.getCertificate().getPublicKey()).getParams().getCurve());
} else if (expectedKey instanceof RSAPrivateKey) {
((ECKey) expectedKey).getParams().getCurve(),
((ECKey) keyEntry.getCertificate().getPublicKey()).getParams().getCurve());
} else if (expectedKey instanceof RSAKey) {
assertEquals("Returned PrivateKey should be what we inserted",
((RSAPrivateKey) expectedKey).getModulus(),
((RSAPrivateKey) keyEntry.getPrivateKey()).getModulus());
((RSAKey) expectedKey).getModulus(),
((RSAKey) keyEntry.getPrivateKey()).getModulus());
}
assertEquals("Returned Certificate should be what we inserted", expectedCert,
@@ -1263,15 +1256,14 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
assertNotNull("Key should exist", key);
assertTrue("Should be a RSAPrivateKey", key instanceof RSAPrivateKey);
RSAPrivateKey actualKey = (RSAPrivateKey) key;
assertTrue("Should be a PrivateKey", key instanceof PrivateKey);
assertTrue("Should be a RSAKey", key instanceof RSAKey);
KeyFactory keyFact = KeyFactory.getInstance("RSA");
PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
assertEquals("Inserted key should be same as retrieved key",
((RSAPrivateKey) expectedKey).getModulus(), actualKey.getModulus());
((RSAKey) expectedKey).getModulus(), ((RSAKey) key).getModulus());
}
public void testKeyStore_GetKey_NoPassword_Unencrypted_Success() throws Exception {
@@ -1287,15 +1279,14 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
assertNotNull("Key should exist", key);
assertTrue("Should be a RSAPrivateKey", key instanceof RSAPrivateKey);
RSAPrivateKey actualKey = (RSAPrivateKey) key;
assertTrue("Should be a PrivateKey", key instanceof PrivateKey);
assertTrue("Should be a RSAKey", key instanceof RSAKey);
KeyFactory keyFact = KeyFactory.getInstance("RSA");
PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
assertEquals("Inserted key should be same as retrieved key",
((RSAPrivateKey) expectedKey).getModulus(), actualKey.getModulus());
((RSAKey) expectedKey).getModulus(), ((RSAKey) key).getModulus());
}
public void testKeyStore_GetKey_Certificate_Encrypted_Failure() throws Exception {
@@ -1926,31 +1917,11 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
Date notAfter) throws Exception {
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
final PrivateKey privKey;
final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
try {
privKey = engine.getPrivateKeyById(privateKeyAlias);
} catch (InvalidKeyException e) {
throw new RuntimeException("Can't get key", e);
}
ExportResult exportResult =
keyStore.exportKey(privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null);
assertEquals(KeyStore.NO_ERROR, exportResult.resultCode);
final byte[] pubKeyBytes = exportResult.exportData;
final PublicKey pubKey;
try {
final KeyFactory keyFact = KeyFactory.getInstance("RSA");
pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Can't instantiate RSA key generator", e);
} catch (InvalidKeySpecException e) {
throw new IllegalStateException("keystore returned invalid key encoding", e);
}
KeyPair keyPair = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
keyStore, privateKeyAlias);
final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
certGen.setPublicKey(pubKey);
certGen.setPublicKey(keyPair.getPublic());
certGen.setSerialNumber(serialNumber);
certGen.setSubjectDN(subjectDN);
certGen.setIssuerDN(subjectDN);
@@ -1958,7 +1929,7 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
certGen.setNotAfter(notAfter);
certGen.setSignatureAlgorithm("sha1WithRSA");
final X509Certificate cert = certGen.generate(privKey);
final X509Certificate cert = certGen.generate(keyPair.getPrivate());
return cert;
}