blob: 1af0b7d4212ad77e54692f9c8483ac088ea8c62b [file] [log] [blame]
/*
* Copyright (C) 2012 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 android.security.Credentials;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore;
import android.security.keymaster.ExportResult;
import android.security.keymaster.KeymasterDefs;
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;
public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
private android.security.KeyStore mAndroidKeyStore;
private java.security.KeyPairGenerator mGenerator;
private static final String TEST_ALIAS_1 = "test1";
private static final String TEST_ALIAS_2 = "test2";
private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
private static final X500Principal TEST_DN_2 = new X500Principal("CN=test2");
private static final BigInteger TEST_SERIAL_1 = BigInteger.ONE;
private static final BigInteger TEST_SERIAL_2 = BigInteger.valueOf(2L);
private static final long NOW_MILLIS = System.currentTimeMillis();
/* We have to round this off because X509v3 doesn't store milliseconds. */
private static final Date NOW = new Date(NOW_MILLIS - (NOW_MILLIS % 1000L));
@SuppressWarnings("deprecation")
private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
@Override
protected void setUp() throws Exception {
mAndroidKeyStore = android.security.KeyStore.getInstance();
assertTrue(mAndroidKeyStore.reset());
assertFalse(mAndroidKeyStore.isUnlocked());
mGenerator = java.security.KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
}
private void setupPassword() {
assertTrue(mAndroidKeyStore.onUserPasswordChanged("1111"));
assertTrue(mAndroidKeyStore.isUnlocked());
String[] aliases = mAndroidKeyStore.list("");
assertNotNull(aliases);
assertEquals(0, aliases.length);
}
public void testKeyPairGenerator_Initialize_Params_Encrypted_Success() throws Exception {
setupPassword();
mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_1)
.setSubject(TEST_DN_1)
.setSerialNumber(TEST_SERIAL_1)
.setStartDate(NOW)
.setEndDate(NOW_PLUS_10_YEARS)
.setEncryptionRequired()
.build());
}
public void testKeyPairGenerator_Initialize_KeySize_Encrypted_Failure() throws Exception {
setupPassword();
try {
mGenerator.initialize(1024);
fail("KeyPairGenerator should not support setting the key size");
} catch (IllegalArgumentException success) {
}
}
public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Encrypted_Failure()
throws Exception {
setupPassword();
try {
mGenerator.initialize(1024, new SecureRandom());
fail("KeyPairGenerator should not support setting the key size");
} catch (IllegalArgumentException success) {
}
}
public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Encrypted_Failure()
throws Exception {
setupPassword();
mGenerator.initialize(
new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_1)
.setKeyType("RSA")
.setKeySize(1024)
.setSubject(TEST_DN_1)
.setSerialNumber(TEST_SERIAL_1)
.setStartDate(NOW)
.setEndDate(NOW_PLUS_10_YEARS)
.setEncryptionRequired()
.build(),
new SecureRandom());
}
public void testKeyPairGenerator_GenerateKeyPair_Encrypted_Success() throws Exception {
setupPassword();
mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_1)
.setSubject(TEST_DN_1)
.setSerialNumber(TEST_SERIAL_1)
.setStartDate(NOW)
.setEndDate(NOW_PLUS_10_YEARS)
.setEncryptionRequired()
.build());
final KeyPair pair = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair);
assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
NOW_PLUS_10_YEARS);
}
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")
.setSubject(TEST_DN_1)
.setSerialNumber(TEST_SERIAL_1)
.setStartDate(NOW)
.setEndDate(NOW_PLUS_10_YEARS)
.build());
final KeyPair pair = mGenerator.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_GenerateKeyPair_EC_P521_Unencrypted_Success() throws Exception {
mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_1)
.setKeyType("EC")
.setKeySize(521)
.setSubject(TEST_DN_1)
.setSerialNumber(TEST_SERIAL_1)
.setStartDate(NOW)
.setEndDate(NOW_PLUS_10_YEARS)
.build());
final KeyPair pair = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair);
assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 521, null, TEST_DN_1, TEST_SERIAL_1, NOW,
NOW_PLUS_10_YEARS);
}
public void testKeyPairGenerator_GenerateKeyPair_RSA_Unencrypted_Success() throws Exception {
mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_1)
.setSubject(TEST_DN_1)
.setSerialNumber(TEST_SERIAL_1)
.setStartDate(NOW)
.setEndDate(NOW_PLUS_10_YEARS)
.build());
final KeyPair pair = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair);
assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
NOW_PLUS_10_YEARS);
}
public void testKeyPairGenerator_GenerateKeyPair_RSA_WithParams_Unencrypted_Success()
throws Exception {
AlgorithmParameterSpec spec = new RSAKeyGenParameterSpec(1024, BigInteger.valueOf(3L));
mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_1)
.setKeySize(1024)
.setAlgorithmParameterSpec(spec)
.setSubject(TEST_DN_1)
.setSerialNumber(TEST_SERIAL_1)
.setStartDate(NOW)
.setEndDate(NOW_PLUS_10_YEARS)
.build());
final KeyPair pair = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair);
assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 1024, spec, TEST_DN_1, TEST_SERIAL_1, NOW,
NOW_PLUS_10_YEARS);
}
public void testKeyPairGenerator_GenerateKeyPair_Replaced_Success() throws Exception {
// Generate the first key
{
mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_1)
.setSubject(TEST_DN_1)
.setSerialNumber(TEST_SERIAL_1)
.setStartDate(NOW)
.setEndDate(NOW_PLUS_10_YEARS)
.build());
final KeyPair pair1 = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair1);
assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1,
NOW, NOW_PLUS_10_YEARS);
}
// Replace the original key
{
mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_2)
.setSubject(TEST_DN_2)
.setSerialNumber(TEST_SERIAL_2)
.setStartDate(NOW)
.setEndDate(NOW_PLUS_10_YEARS)
.build());
final KeyPair pair2 = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair2);
assertKeyPairCorrect(pair2, TEST_ALIAS_2, "RSA", 2048, null, TEST_DN_2, TEST_SERIAL_2,
NOW, NOW_PLUS_10_YEARS);
}
}
public void testKeyPairGenerator_GenerateKeyPair_Replaced_UnencryptedToEncrypted_Success()
throws Exception {
// Generate the first key
{
mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_1)
.setSubject(TEST_DN_1)
.setSerialNumber(TEST_SERIAL_1)
.setStartDate(NOW)
.setEndDate(NOW_PLUS_10_YEARS)
.build());
final KeyPair pair1 = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair1);
assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1,
NOW, NOW_PLUS_10_YEARS);
}
// Attempt to replace previous key
{
mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
.setAlias(TEST_ALIAS_1)
.setSubject(TEST_DN_2)
.setSerialNumber(TEST_SERIAL_2)
.setStartDate(NOW)
.setEndDate(NOW_PLUS_10_YEARS)
.setEncryptionRequired()
.build());
try {
mGenerator.generateKeyPair();
fail("Should not be able to generate encrypted key while not initialized");
} catch (IllegalStateException expected) {
}
assertTrue(mAndroidKeyStore.onUserPasswordChanged("1111"));
assertTrue(mAndroidKeyStore.isUnlocked());
final KeyPair pair2 = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair2);
assertKeyPairCorrect(pair2, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_2, TEST_SERIAL_2,
NOW, NOW_PLUS_10_YEARS);
}
}
private void assertKeyPairCorrect(KeyPair pair, String alias, String keyType, int keySize,
AlgorithmParameterSpec spec, X500Principal dn, BigInteger serial, Date start, Date end)
throws Exception {
final PublicKey pubKey = pair.getPublic();
assertNotNull("The PublicKey for the KeyPair should be not null", pubKey);
assertEquals(keyType, pubKey.getAlgorithm());
if ("EC".equalsIgnoreCase(keyType)) {
assertEquals("Curve should be what was specified during initialization", keySize,
((ECPublicKey) pubKey).getParams().getCurve().getField().getFieldSize());
} else if ("RSA".equalsIgnoreCase(keyType)) {
RSAPublicKey rsaPubKey = (RSAPublicKey) pubKey;
assertEquals("Modulus size should be what is specified during initialization",
(keySize + 7) & ~7, (rsaPubKey.getModulus().bitLength() + 7) & ~7);
if (spec != null) {
RSAKeyGenParameterSpec params = (RSAKeyGenParameterSpec) spec;
assertEquals((keySize + 7) & ~7, (params.getKeysize() + 7) & ~7);
assertEquals(params.getPublicExponent(), rsaPubKey.getPublicExponent());
}
}
final PrivateKey privKey = pair.getPrivate();
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));
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,
AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
Credentials.USER_PRIVATE_KEY + alias,
KeyStore.UID_SELF,
x509userCert.getPublicKey().getAlgorithm(),
x509userCert.getPublicKey().getEncoded()));
assertEquals("The Subject DN should be the one passed into the params", dn,
x509userCert.getSubjectDN());
assertEquals("The Issuer DN should be the same as the Subject DN", dn,
x509userCert.getIssuerDN());
assertEquals("The Serial should be the one passed into the params", serial,
x509userCert.getSerialNumber());
assertDateEquals("The notBefore date should be the one passed into the params", start,
x509userCert.getNotBefore());
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);
ExportResult exportResult = mAndroidKeyStore.exportKey(
Credentials.USER_PRIVATE_KEY + alias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null);
assertEquals(KeyStore.NO_ERROR, exportResult.resultCode);
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 {
SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
String result1 = formatter.format(date1);
String result2 = formatter.format(date2);
assertEquals(message, result1, result2);
}
}