diff options
15 files changed, 675 insertions, 202 deletions
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 059d8e6..f482bf0 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -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(); diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java index efbce41..d849317 100644 --- a/keystore/java/android/security/KeyPairGeneratorSpec.java +++ b/keystore/java/android/security/KeyPairGeneratorSpec.java @@ -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); } } diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 893771a..35fcda6 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -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 { diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECPrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreECPrivateKey.java new file mode 100644 index 0000000..5dbcd68 --- /dev/null +++ b/keystore/java/android/security/keystore/AndroidKeyStoreECPrivateKey.java @@ -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; + } +} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreKey.java index 1751aa5..e76802f 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKey.java @@ -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; + } } diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index 69155a8..35af34f 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -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) { - 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) { + if (!mJcaKeyAlgorithm.equalsIgnoreCase(result.getPrivate().getAlgorithm())) { 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 { diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java index cb270bb..967319a 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java @@ -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); + } } diff --git a/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java index 8133d46..9fea30d 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java +++ b/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java @@ -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; + } } diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPrivateKey.java new file mode 100644 index 0000000..179ffd8 --- /dev/null +++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPrivateKey.java @@ -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; + } +} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java index 4c4062f..f072ae7 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java @@ -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; diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java index c03be63..831a106 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java @@ -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()); + } + } } diff --git a/keystore/java/android/security/keystore/DelegatingX509Certificate.java b/keystore/java/android/security/keystore/DelegatingX509Certificate.java new file mode 100644 index 0000000..03d202f --- /dev/null +++ b/keystore/java/android/security/keystore/DelegatingX509Certificate.java @@ -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(); + } +} diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java index 44fb826..0b60c62 100644 --- a/keystore/tests/src/android/security/KeyStoreTest.java +++ b/keystore/tests/src/android/security/KeyStoreTest.java @@ -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 * diff --git a/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java b/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java index 8488acd..e5c15c5 100644 --- a/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java +++ b/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java @@ -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 { diff --git a/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java index 336fa40..c3b731b 100644 --- a/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java +++ b/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java @@ -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; } |