CTS-test Android Keystore import supports required key sizes.
This explicitly tests that Android Keystore can import keys of all
required sizes:
* AES: 128, 192, 256.
* HMAC: 8 -- 1024 (must be multiple of 8).
* EC: P-224, P-256, P-384, P-521.
* RSA: 512, 768, 1024, 2048, 3072, 4096.
For AES, this also tests that other sizes are not supported.
Bug: 21936191
Change-Id: I923437c86b438e77a2a9027bb9f9fb4742138881
diff --git a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
index 6cd64b8..5fe3505 100644
--- a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
@@ -62,7 +62,6 @@
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.x500.X500Principal;
public class AndroidKeyStoreTest extends AndroidTestCase {
@@ -1888,7 +1887,7 @@
0x00, 0x05, (byte) 0xAA, (byte) 0x0A5, (byte) 0xFF, 0x55, 0x0A
};
- SecretKey expectedSecret = new SecretKeySpec(expectedKey, "AES");
+ SecretKey expectedSecret = new TransparentSecretKey(expectedKey, "AES");
byte[] wrappedExpected = c.wrap(expectedSecret);
@@ -2231,15 +2230,15 @@
long testStartTimeMillis = System.currentTimeMillis();
- SecretKey key1 =
- new SecretKeySpec(HexEncoding.decode("010203040506070809fafbfcfdfeffcc"), "AES");
+ SecretKey key1 = new TransparentSecretKey(
+ HexEncoding.decode("010203040506070809fafbfcfdfeffcc"), "AES");
String entryName1 = "test0";
- SecretKey key2 =
- new SecretKeySpec(HexEncoding.decode("808182838485868788897a7b7c7d7e7f"), "AES");
+ SecretKey key2 = new TransparentSecretKey(
+ HexEncoding.decode("808182838485868788897a7b7c7d7e7f"), "AES");
- SecretKey key3 =
- new SecretKeySpec(HexEncoding.decode("33333333333333333333777777777777"), "AES");
+ SecretKey key3 = new TransparentSecretKey(
+ HexEncoding.decode("33333333333333333333777777777777"), "AES");
mKeyStore.load(null);
int latestImportedEntryNumber = 0;
@@ -2322,14 +2321,14 @@
long testStartTimeMillis = System.currentTimeMillis();
- SecretKey key1 = new SecretKeySpec(
+ SecretKey key1 = new TransparentSecretKey(
HexEncoding.decode("010203040506070809fafbfcfdfeffcc"), "HmacSHA256");
String entryName1 = "test0";
- SecretKey key2 = new SecretKeySpec(
+ SecretKey key2 = new TransparentSecretKey(
HexEncoding.decode("808182838485868788897a7b7c7d7e7f"), "HmacSHA256");
- SecretKey key3 = new SecretKeySpec(
+ SecretKey key3 = new TransparentSecretKey(
HexEncoding.decode("33333333333333333333777777777777"), "HmacSHA256");
mKeyStore.load(null);
@@ -2406,7 +2405,7 @@
try {
String digest = TestUtils.getHmacAlgorithmDigest(algorithm);
assertNotNull(digest);
- SecretKeySpec keyBeingImported = new SecretKeySpec(new byte[16], algorithm);
+ SecretKey keyBeingImported = new TransparentSecretKey(new byte[16], algorithm);
KeyProtection.Builder goodSpec =
new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN);
@@ -2457,4 +2456,121 @@
}
}
}
+
+ public void testKeyStore_ImportSupportedSizes_AES() throws Exception {
+ mKeyStore.load(null);
+
+ KeyProtection params = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .build();
+ String alias = "test1";
+ mKeyStore.deleteEntry(alias);
+ assertFalse(mKeyStore.containsAlias(alias));
+ for (int keySizeBytes = 0; keySizeBytes <= 512 / 8; keySizeBytes++) {
+ int keySizeBits = keySizeBytes * 8;
+ try {
+ KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(
+ new TransparentSecretKey(new byte[keySizeBytes], "AES"));
+ if (TestUtils.contains(KeyGeneratorTest.AES_SUPPORTED_KEY_SIZES, keySizeBits)) {
+ mKeyStore.setEntry(alias, entry, params);
+ SecretKey key = (SecretKey) mKeyStore.getKey(alias, null);
+ assertEquals("AES", key.getAlgorithm());
+ assertEquals(keySizeBits, TestUtils.getKeyInfo(key).getKeySize());
+ } else {
+ mKeyStore.deleteEntry(alias);
+ assertFalse(mKeyStore.containsAlias(alias));
+ try {
+ mKeyStore.setEntry(alias, entry, params);
+ fail();
+ } catch (KeyStoreException expected) {}
+ assertFalse(mKeyStore.containsAlias(alias));
+ }
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for key size " + keySizeBits, e);
+ }
+ }
+ }
+
+ public void testKeyStore_ImportSupportedSizes_HMAC() throws Exception {
+ mKeyStore.load(null);
+
+ KeyProtection params = new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN).build();
+ String alias = "test1";
+ mKeyStore.deleteEntry(alias);
+ assertFalse(mKeyStore.containsAlias(alias));
+ for (String algorithm : KeyGeneratorTest.EXPECTED_ALGORITHMS) {
+ if (!TestUtils.isHmacAlgorithm(algorithm)) {
+ continue;
+ }
+ for (int keySizeBytes = 0; keySizeBytes <= 1024 / 8; keySizeBytes++) {
+ try {
+ KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(
+ new TransparentSecretKey(new byte[keySizeBytes], algorithm));
+ if (keySizeBytes > 0) {
+ mKeyStore.setEntry(alias, entry, params);
+ SecretKey key = (SecretKey) mKeyStore.getKey(alias, null);
+ assertEquals(algorithm, key.getAlgorithm());
+ assertEquals(keySizeBytes * 8, TestUtils.getKeyInfo(key).getKeySize());
+ } else {
+ mKeyStore.deleteEntry(alias);
+ assertFalse(mKeyStore.containsAlias(alias));
+ try {
+ mKeyStore.setEntry(alias, entry, params);
+ fail();
+ } catch (KeyStoreException expected) {}
+ }
+ } catch (Throwable e) {
+ throw new RuntimeException(
+ "Failed for " + algorithm + " with key size " + (keySizeBytes * 8), e);
+ }
+ }
+ }
+ }
+
+ public void testKeyStore_ImportSupportedSizes_EC() throws Exception {
+ mKeyStore.load(null);
+ KeyProtection params =
+ TestUtils.getMinimalWorkingImportParametersForSigningingWith("SHA256withECDSA");
+ checkKeyPairImportSucceeds(
+ "secp224r1", R.raw.ec_key3_secp224r1_pkcs8, R.raw.ec_key3_secp224r1_cert, params);
+ checkKeyPairImportSucceeds(
+ "secp256r1", R.raw.ec_key4_secp256r1_pkcs8, R.raw.ec_key4_secp256r1_cert, params);
+ checkKeyPairImportSucceeds(
+ "secp384r1", R.raw.ec_key5_secp384r1_pkcs8, R.raw.ec_key5_secp384r1_cert, params);
+ checkKeyPairImportSucceeds(
+ "secp512r1", R.raw.ec_key6_secp521r1_pkcs8, R.raw.ec_key6_secp521r1_cert, params);
+ }
+
+ public void testKeyStore_ImportSupportedSizes_RSA() throws Exception {
+ mKeyStore.load(null);
+ KeyProtection params =
+ TestUtils.getMinimalWorkingImportParametersForSigningingWith("SHA256withRSA");
+ checkKeyPairImportSucceeds(
+ "512", R.raw.rsa_key5_512_pkcs8, R.raw.rsa_key5_512_cert, params);
+ checkKeyPairImportSucceeds(
+ "768", R.raw.rsa_key6_768_pkcs8, R.raw.rsa_key6_768_cert, params);
+ checkKeyPairImportSucceeds(
+ "1024", R.raw.rsa_key3_1024_pkcs8, R.raw.rsa_key3_1024_cert, params);
+ checkKeyPairImportSucceeds(
+ "2048", R.raw.rsa_key8_2048_pkcs8, R.raw.rsa_key8_2048_cert, params);
+ checkKeyPairImportSucceeds(
+ "3072", R.raw.rsa_key7_3072_pksc8, R.raw.rsa_key7_3072_cert, params);
+ checkKeyPairImportSucceeds(
+ "4096", R.raw.rsa_key4_4096_pkcs8, R.raw.rsa_key4_4096_cert, params);
+ }
+
+ private void checkKeyPairImportSucceeds(
+ String alias, int privateResId, int certResId, KeyProtection params) throws Exception {
+ try {
+ mKeyStore.deleteEntry(alias);
+ TestUtils.importIntoAndroidKeyStore(
+ alias, getContext(), privateResId, certResId, params);
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + alias, e);
+ } finally {
+ try {
+ mKeyStore.deleteEntry(alias);
+ } catch (Exception ignored) {}
+ }
+ }
}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java
index 88d5421..6deaed4 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java
@@ -66,7 +66,7 @@
DEFAULT_KEY_SIZES.put("HmacSHA512", 512);
}
- private static final int[] AES_SUPPORTED_KEY_SIZES = new int[] {128, 192, 256};
+ static final int[] AES_SUPPORTED_KEY_SIZES = new int[] {128, 192, 256};
public void testAlgorithmList() {
// Assert that Android Keystore Provider exposes exactly the expected KeyGenerator
@@ -244,7 +244,7 @@
assertEquals(0, rng.getOutputSizeBytes());
}
} catch (Throwable e) {
- throw new RuntimeException("Failed to key size " + i, e);
+ throw new RuntimeException("Failed for key size " + i, e);
}
}
}
diff --git a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
index 38905bf..1c4f6ad 100644
--- a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
+++ b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
@@ -27,7 +27,6 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
@@ -53,7 +52,6 @@
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -442,29 +440,13 @@
+ " Public: " + originalPublicKey.getAlgorithm()
+ ", private: " + originalPrivateKey.getAlgorithm());
}
- String keyAlgorithm = originalPublicKey.getAlgorithm();
- if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
- ECParameterSpec publicKeyParams = ((ECKey) originalPublicKey).getParams();
- ECParameterSpec privateKeyParams = ((ECKey) originalPrivateKey).getParams();
- assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
- publicKeyParams, privateKeyParams);
- } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
- BigInteger publicKeyModulus = ((RSAKey) originalPublicKey).getModulus();
- BigInteger privateKeyModulus = ((RSAKey) originalPrivateKey).getModulus();
- if (!publicKeyModulus.equals(privateKeyModulus)) {
- throw new IllegalArgumentException("RSA key pair modulus mismatch."
- + " Public (" + publicKeyModulus.bitLength() + " bit): "
- + publicKeyModulus.toString(16)
- + ", private (" + privateKeyModulus.bitLength() + " bit): "
- + privateKeyModulus.toString(16));
- }
- } else {
- throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
- }
+ assertKeyPairSelfConsistent(originalPublicKey, originalPrivateKey);
KeyPair keystoreBacked = TestUtils.importIntoAndroidKeyStore(
alias, originalPrivateKey, originalCert,
params);
+ assertKeyPairSelfConsistent(keystoreBacked);
+ assertKeyPairSelfConsistent(keystoreBacked.getPublic(), originalPrivateKey);
return new ImportedKey(
alias,
new KeyPair(originalCert.getPublicKey(), originalPrivateKey),
diff --git a/tests/tests/keystore/src/android/keystore/cts/TransparentSecretKey.java b/tests/tests/keystore/src/android/keystore/cts/TransparentSecretKey.java
new file mode 100644
index 0000000..6e74dc0
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/TransparentSecretKey.java
@@ -0,0 +1,50 @@
+/*
+ * 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 javax.crypto.SecretKey;
+
+/**
+ * {@link SecretKey} which exposes its key material. The two reasons for the existence of this class
+ * are: (1) to help test that classes under test don't assume that all transparent secret keys are
+ * instances of {@link SecretKeySpec}, and (2) because {@code SecretKeySpec} rejects zero-length
+ * key material which is needed in some tests.
+ */
+public class TransparentSecretKey implements SecretKey {
+ private final String mAlgorithm;
+ private final byte[] mKeyMaterial;
+
+ public TransparentSecretKey(byte[] keyMaterial, String algorithm) {
+ mAlgorithm = algorithm;
+ mKeyMaterial = keyMaterial.clone();
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return mAlgorithm;
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ return mKeyMaterial;
+ }
+
+ @Override
+ public String getFormat() {
+ return "RAW";
+ }
+}