CTS tests for Android Keystore KeyFactory and SecretKeyFactory.
Bug: 21936191
Change-Id: Ie40da4b269b64f92e2ee49677fefbcd35940f51d
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyFactoryTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyFactoryTest.java
new file mode 100644
index 0000000..ebf3688
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyFactoryTest.java
@@ -0,0 +1,464 @@
+/*
+ * 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.keystore.cts;
+
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyInfo;
+import android.security.keystore.KeyProperties;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+
+import com.android.cts.keystore.R;
+
+import java.io.InputStream;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.Security;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.security.Provider.Service;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+public class KeyFactoryTest extends AndroidTestCase {
+ private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_PROVIDER_NAME;
+
+ private static final String[] EXPECTED_ALGORITHMS = {
+ "EC",
+ "RSA",
+ };
+
+ public void testAlgorithmList() {
+ // Assert that Android Keystore Provider exposes exactly the expected KeyFactory algorithms.
+ // We don't care whether the algorithms are exposed via aliases, as long as canonical names
+ // of algorithms are accepted. If the Provider exposes extraneous algorithms, it'll be
+ // caught because it'll have to expose at least one Service for such an algorithm, and this
+ // Service's algorithm will not be in the expected set.
+
+ Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+ Set<Service> services = provider.getServices();
+ Set<String> actualAlgsLowerCase = new HashSet<String>();
+ Set<String> expectedAlgsLowerCase = new HashSet<String>(
+ Arrays.asList(TestUtils.toLowerCase(EXPECTED_ALGORITHMS)));
+ for (Service service : services) {
+ if ("KeyFactory".equalsIgnoreCase(service.getType())) {
+ String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
+ actualAlgsLowerCase.add(algLowerCase);
+ }
+ }
+
+ TestUtils.assertContentsInAnyOrder(actualAlgsLowerCase,
+ expectedAlgsLowerCase.toArray(new String[0]));
+ }
+
+ public void testGetKeySpecWithKeystorePrivateKeyAndKeyInfoReflectsAllAuthorizations()
+ throws Exception {
+ Date keyValidityStart = new Date(System.currentTimeMillis() - TestUtils.DAY_IN_MILLIS);
+ Date keyValidityForOriginationEnd =
+ new Date(System.currentTimeMillis() + TestUtils.DAY_IN_MILLIS);
+ Date keyValidityForConsumptionEnd =
+ new Date(System.currentTimeMillis() + 3 * TestUtils.DAY_IN_MILLIS);
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ String[] blockModes = new String[] {KeyProperties.BLOCK_MODE_ECB};
+ String[] encryptionPaddings =
+ new String[] {KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
+ KeyProperties.ENCRYPTION_PADDING_RSA_OAEP};
+ String[] digests = new String[] {KeyProperties.DIGEST_SHA1,
+ KeyProperties.DIGEST_SHA224,
+ KeyProperties.DIGEST_SHA384,
+ KeyProperties.DIGEST_SHA512};
+ int purposes = KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_SIGN;
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", purposes)
+ .setBlockModes(blockModes)
+ .setEncryptionPaddings(encryptionPaddings)
+ .setDigests(digests)
+ .setKeyValidityStart(keyValidityStart)
+ .setKeyValidityForOriginationEnd(keyValidityForOriginationEnd)
+ .setKeyValidityForConsumptionEnd(keyValidityForConsumptionEnd)
+ .build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ KeyInfo keyInfo = keyFactory.getKeySpec(keyPair.getPrivate(), KeyInfo.class);
+ assertEquals("test1", keyInfo.getKeystoreAlias());
+ assertEquals(purposes, keyInfo.getPurposes());
+ TestUtils.assertContentsInAnyOrder(
+ Arrays.asList(blockModes), keyInfo.getBlockModes());
+ TestUtils.assertContentsInAnyOrder(
+ Arrays.asList(encryptionPaddings), keyInfo.getEncryptionPaddings());
+ TestUtils.assertContentsInAnyOrder(Arrays.asList(digests), keyInfo.getDigests());
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+ assertEquals(keyValidityStart, keyInfo.getKeyValidityStart());
+ assertEquals(keyValidityForOriginationEnd,
+ keyInfo.getKeyValidityForOriginationEnd());
+ assertEquals(keyValidityForConsumptionEnd,
+ keyInfo.getKeyValidityForConsumptionEnd());
+ assertFalse(keyInfo.isUserAuthenticationRequired());
+ assertFalse(keyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware());
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGetKeySpecWithKeystorePublicKeyRejectsKeyInfo()
+ throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", 0).build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.getKeySpec(keyPair.getPublic(), KeyInfo.class);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGetKeySpecWithKeystorePrivateKeyRejectsTransparentKeySpecAndEncodedKeySpec()
+ throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ Class<? extends KeySpec> transparentKeySpecClass;
+ if ("EC".equalsIgnoreCase(algorithm)) {
+ transparentKeySpecClass = ECPrivateKeySpec.class;
+ } else if ("RSA".equalsIgnoreCase(algorithm)) {
+ transparentKeySpecClass = RSAPrivateKeySpec.class;
+ } else {
+ throw new RuntimeException("Unsupported key algorithm: " + algorithm);
+ }
+
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", 0).build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.getKeySpec(keyPair.getPrivate(), transparentKeySpecClass);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+
+ try {
+ keyFactory.getKeySpec(keyPair.getPrivate(), PKCS8EncodedKeySpec.class);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGetKeySpecWithKeystorePublicKeyAcceptsX509EncodedKeySpec()
+ throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", 0).build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+ PublicKey publicKey = keyPair.getPublic();
+
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ X509EncodedKeySpec x509EncodedSpec =
+ keyFactory.getKeySpec(publicKey, X509EncodedKeySpec.class);
+ MoreAsserts.assertEquals(publicKey.getEncoded(), x509EncodedSpec.getEncoded());
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGetKeySpecWithKeystorePublicKeyAcceptsTransparentKeySpec()
+ throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", 0).build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+ PublicKey publicKey = keyPair.getPublic();
+
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ if ("EC".equalsIgnoreCase(algorithm)) {
+ ECPublicKey ecPublicKey = (ECPublicKey) publicKey;
+ ECPublicKeySpec spec =
+ keyFactory.getKeySpec(publicKey, ECPublicKeySpec.class);
+ assertEquals(ecPublicKey.getW(), spec.getW());
+ TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+ ecPublicKey.getParams(), spec.getParams());
+ } else if ("RSA".equalsIgnoreCase(algorithm)) {
+ RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
+ RSAPublicKeySpec spec =
+ keyFactory.getKeySpec(publicKey, RSAPublicKeySpec.class);
+ assertEquals(rsaPublicKey.getModulus(), spec.getModulus());
+ assertEquals(rsaPublicKey.getPublicExponent(), spec.getPublicExponent());
+ } else {
+ throw new RuntimeException("Unsupported key algorithm: " + algorithm);
+ }
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testTranslateKeyWithNullKeyThrowsInvalidKeyException() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.translateKey(null);
+ fail();
+ } catch (InvalidKeyException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testTranslateKeyRejectsNonAndroidKeystoreKeys() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ SecretKey key = new SecretKeySpec(new byte[16], algorithm);
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.translateKey(key);
+ fail();
+ } catch (InvalidKeyException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testTranslateKeyAcceptsAndroidKeystoreKeys() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", 0).build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ assertSame(keyPair.getPrivate(), keyFactory.translateKey(keyPair.getPrivate()));
+ assertSame(keyPair.getPublic(), keyFactory.translateKey(keyPair.getPublic()));
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePrivateWithNullSpecThrowsInvalidKeySpecException() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePrivate(null);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePublicWithNullSpecThrowsInvalidKeySpecException() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePublic(null);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePrivateRejectsPKCS8EncodedKeySpec() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ int resId;
+ if ("EC".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.ec_key1_pkcs8;
+ } else if ("RSA".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.rsa_key2_pkcs8;
+ } else {
+ throw new RuntimeException("Unsupported key algorithm: " + algorithm);
+ }
+
+ byte[] pkcs8EncodedForm;
+ try (InputStream in = getContext().getResources().openRawResource(resId)) {
+ pkcs8EncodedForm = TestUtils.drain(in);
+ }
+ PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkcs8EncodedForm);
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePrivate(spec);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePublicRejectsX509EncodedKeySpec() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ int resId;
+ if ("EC".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.ec_key2_cert;
+ } else if ("RSA".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.rsa_key1_cert;
+ } else {
+ throw new RuntimeException("Unsupported key algorithm: " + algorithm);
+ }
+
+ byte[] x509EncodedForm;
+ try (InputStream in = getContext().getResources().openRawResource(resId)) {
+ x509EncodedForm = TestUtils.drain(in);
+ }
+ X509EncodedKeySpec spec = new X509EncodedKeySpec(x509EncodedForm);
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePublic(spec);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePrivateRejectsTransparentKeySpec() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ int resId;
+ Class<? extends KeySpec> keySpecClass;
+ if ("EC".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.ec_key2_pkcs8;
+ keySpecClass = ECPrivateKeySpec.class;
+ } else if ("RSA".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.rsa_key2_pkcs8;
+ keySpecClass = RSAPrivateKeySpec.class;
+ } else {
+ throw new RuntimeException("Unsupported key algorithm: " + algorithm);
+ }
+ PrivateKey key = TestUtils.getRawResPrivateKey(getContext(), resId);
+ KeyFactory anotherKeyFactory = KeyFactory.getInstance(algorithm);
+ KeySpec spec = anotherKeyFactory.getKeySpec(key, keySpecClass);
+
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePrivate(spec);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePublicRejectsTransparentKeySpec() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ int resId;
+ Class<? extends KeySpec> keySpecClass;
+ if ("EC".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.ec_key2_cert;
+ keySpecClass = ECPublicKeySpec.class;
+ } else if ("RSA".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.rsa_key2_cert;
+ keySpecClass = RSAPublicKeySpec.class;
+ } else {
+ throw new RuntimeException("Unsupported key algorithm: " + algorithm);
+ }
+ PublicKey key = TestUtils.getRawResX509Certificate(getContext(), resId).getPublicKey();
+ KeyFactory anotherKeyFactory = KeyFactory.getInstance(algorithm);
+ KeySpec spec = anotherKeyFactory.getKeySpec(key, keySpecClass);
+
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePublic(spec);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePrivateAndPublicRejectKeyInfo() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", 0).build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePrivate(keyInfo);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+
+ try {
+ keyFactory.generatePublic(keyInfo);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ private KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException,
+ NoSuchProviderException {
+ return KeyFactory.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/keystore/src/android/keystore/cts/SecretKeyFactoryTest.java b/tests/tests/keystore/src/android/keystore/cts/SecretKeyFactoryTest.java
new file mode 100644
index 0000000..2bb8ecd
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/SecretKeyFactoryTest.java
@@ -0,0 +1,236 @@
+/*
+ * 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.keystore.cts;
+
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyInfo;
+import android.security.keystore.KeyProperties;
+import android.test.MoreAsserts;
+
+import junit.framework.TestCase;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.Security;
+import java.security.spec.InvalidKeySpecException;
+import java.security.Provider.Service;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.SecretKeySpec;
+
+public class SecretKeyFactoryTest extends TestCase {
+ private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_PROVIDER_NAME;
+
+ private static final String[] EXPECTED_ALGORITHMS = {
+ "AES",
+ "HmacSHA1",
+ "HmacSHA224",
+ "HmacSHA256",
+ "HmacSHA384",
+ "HmacSHA512",
+ };
+
+ public void testAlgorithmList() {
+ // Assert that Android Keystore Provider exposes exactly the expected SecretKeyFactory
+ // algorithms. We don't care whether the algorithms are exposed via aliases, as long as
+ // canonical names of algorithms are accepted. If the Provider exposes extraneous
+ // algorithms, it'll be caught because it'll have to expose at least one Service for such an
+ // algorithm, and this Service's algorithm will not be in the expected set.
+
+ Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+ Set<Service> services = provider.getServices();
+ Set<String> actualAlgsLowerCase = new HashSet<String>();
+ Set<String> expectedAlgsLowerCase = new HashSet<String>(
+ Arrays.asList(TestUtils.toLowerCase(EXPECTED_ALGORITHMS)));
+ for (Service service : services) {
+ if ("SecretKeyFactory".equalsIgnoreCase(service.getType())) {
+ String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
+ actualAlgsLowerCase.add(algLowerCase);
+ }
+ }
+
+ TestUtils.assertContentsInAnyOrder(actualAlgsLowerCase,
+ expectedAlgsLowerCase.toArray(new String[0]));
+ }
+
+ public void testGetKeySpecWithKeystoreKeyAndKeyInfoReflectsAllAuthorizations()
+ throws Exception {
+ Date keyValidityStart = new Date(System.currentTimeMillis() - TestUtils.DAY_IN_MILLIS);
+ Date keyValidityForOriginationEnd =
+ new Date(System.currentTimeMillis() + TestUtils.DAY_IN_MILLIS);
+ Date keyValidityForConsumptionEnd =
+ new Date(System.currentTimeMillis() + 3 * TestUtils.DAY_IN_MILLIS);
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ String[] blockModes =
+ new String[] {KeyProperties.BLOCK_MODE_CTR, KeyProperties.BLOCK_MODE_ECB};
+ String[] encryptionPaddings =
+ new String[] {KeyProperties.ENCRYPTION_PADDING_PKCS7,
+ KeyProperties.ENCRYPTION_PADDING_NONE};
+ String[] digests;
+ int purposes;
+ if (TestUtils.isHmacAlgorithm(algorithm)) {
+ String digest = TestUtils.getHmacAlgorithmDigest(algorithm);
+ String anotherDigest = KeyProperties.DIGEST_SHA256.equalsIgnoreCase(digest)
+ ? KeyProperties.DIGEST_SHA512 : KeyProperties.DIGEST_SHA256;
+ digests = new String[] {anotherDigest, digest};
+ purposes = KeyProperties.PURPOSE_SIGN;
+ } else {
+ digests = new String[] {KeyProperties.DIGEST_SHA384};
+ purposes = KeyProperties.PURPOSE_DECRYPT;
+ }
+ KeyGenerator keyGenerator =
+ KeyGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.init(new KeyGenParameterSpec.Builder("test1", purposes)
+ .setBlockModes(blockModes)
+ .setEncryptionPaddings(encryptionPaddings)
+ .setDigests(digests)
+ .setKeyValidityStart(keyValidityStart)
+ .setKeyValidityForOriginationEnd(keyValidityForOriginationEnd)
+ .setKeyValidityForConsumptionEnd(keyValidityForConsumptionEnd)
+ .build());
+ SecretKey key = keyGenerator.generateKey();
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ KeyInfo keyInfo = (KeyInfo) keyFactory.getKeySpec(key, KeyInfo.class);
+ assertEquals("test1", keyInfo.getKeystoreAlias());
+ assertEquals(purposes, keyInfo.getPurposes());
+ TestUtils.assertContentsInAnyOrder(
+ Arrays.asList(blockModes), keyInfo.getBlockModes());
+ TestUtils.assertContentsInAnyOrder(
+ Arrays.asList(encryptionPaddings), keyInfo.getEncryptionPaddings());
+ TestUtils.assertContentsInAnyOrder(Arrays.asList(digests), keyInfo.getDigests());
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+ assertEquals(keyValidityStart, keyInfo.getKeyValidityStart());
+ assertEquals(keyValidityForOriginationEnd,
+ keyInfo.getKeyValidityForOriginationEnd());
+ assertEquals(keyValidityForConsumptionEnd,
+ keyInfo.getKeyValidityForConsumptionEnd());
+ assertFalse(keyInfo.isUserAuthenticationRequired());
+ assertFalse(keyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware());
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testTranslateKeyWithNullKeyThrowsInvalidKeyException() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.translateKey(null);
+ fail();
+ } catch (InvalidKeyException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testTranslateKeyRejectsNonAndroidKeystoreKeys() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ SecretKey key = new SecretKeySpec(new byte[16], algorithm);
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.translateKey(key);
+ fail();
+ } catch (InvalidKeyException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testTranslateKeyAcceptsAndroidKeystoreKeys() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyGenerator keyGenerator =
+ KeyGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.init(new KeyGenParameterSpec.Builder("test1", 0).build());
+ SecretKey key = keyGenerator.generateKey();
+
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ assertSame(key, keyFactory.translateKey(key));
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGenerateSecretWithNullSpecThrowsInvalidKeySpecException() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generateSecret(null);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGenerateSecretRejectsSecretKeySpec() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generateSecret(new SecretKeySpec(new byte[16], algorithm));
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGenerateSecretRejectsKeyInfo() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyGenerator keyGenerator =
+ KeyGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.init(new KeyGenParameterSpec.Builder("test1", 0).build());
+ SecretKey keystoreKey = keyGenerator.generateKey();
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keystoreKey);
+
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generateSecret(keyInfo);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ private SecretKeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException,
+ NoSuchProviderException {
+ return SecretKeyFactory.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ }
+}