1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.security.keystore;
18
19import android.security.Credentials;
20import android.security.KeyPairGeneratorSpec;
21import android.security.KeyStore;
22import android.security.keymaster.ExportResult;
23import android.security.keymaster.KeymasterDefs;
24import android.test.AndroidTestCase;
25
26import java.io.ByteArrayInputStream;
27import java.math.BigInteger;
28import java.security.KeyPair;
29import java.security.KeyPairGenerator;
30import java.security.PrivateKey;
31import java.security.PublicKey;
32import java.security.SecureRandom;
33import java.security.cert.Certificate;
34import java.security.cert.CertificateFactory;
35import java.security.cert.X509Certificate;
36import java.security.interfaces.ECKey;
37import java.security.interfaces.ECPublicKey;
38import java.security.interfaces.RSAKey;
39import java.security.interfaces.RSAPublicKey;
40import java.security.spec.AlgorithmParameterSpec;
41import java.security.spec.RSAKeyGenParameterSpec;
42import java.text.SimpleDateFormat;
43import java.util.Arrays;
44import java.util.Date;
45
46import javax.security.auth.x500.X500Principal;
47
48public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
49    private android.security.KeyStore mAndroidKeyStore;
50
51    private java.security.KeyPairGenerator mGenerator;
52
53    private static final String TEST_ALIAS_1 = "test1";
54
55    private static final String TEST_ALIAS_2 = "test2";
56
57    private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
58
59    private static final X500Principal TEST_DN_2 = new X500Principal("CN=test2");
60
61    private static final BigInteger TEST_SERIAL_1 = BigInteger.ONE;
62
63    private static final BigInteger TEST_SERIAL_2 = BigInteger.valueOf(2L);
64
65    private static final long NOW_MILLIS = System.currentTimeMillis();
66
67    /* We have to round this off because X509v3 doesn't store milliseconds. */
68    private static final Date NOW = new Date(NOW_MILLIS - (NOW_MILLIS % 1000L));
69
70    @SuppressWarnings("deprecation")
71    private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
72
73    @Override
74    protected void setUp() throws Exception {
75        mAndroidKeyStore = android.security.KeyStore.getInstance();
76
77        assertTrue(mAndroidKeyStore.reset());
78
79        assertFalse(mAndroidKeyStore.isUnlocked());
80
81        mGenerator = java.security.KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
82    }
83
84    private void setupPassword() {
85        assertTrue(mAndroidKeyStore.onUserPasswordChanged("1111"));
86        assertTrue(mAndroidKeyStore.isUnlocked());
87
88        String[] aliases = mAndroidKeyStore.list("");
89        assertNotNull(aliases);
90        assertEquals(0, aliases.length);
91    }
92
93    public void testKeyPairGenerator_Initialize_Params_Encrypted_Success() throws Exception {
94        setupPassword();
95
96        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
97                .setAlias(TEST_ALIAS_1)
98                .setSubject(TEST_DN_1)
99                .setSerialNumber(TEST_SERIAL_1)
100                .setStartDate(NOW)
101                .setEndDate(NOW_PLUS_10_YEARS)
102                .setEncryptionRequired()
103                .build());
104    }
105
106    public void testKeyPairGenerator_Initialize_KeySize_Encrypted_Failure() throws Exception {
107        setupPassword();
108
109        try {
110            mGenerator.initialize(1024);
111            fail("KeyPairGenerator should not support setting the key size");
112        } catch (IllegalArgumentException success) {
113        }
114    }
115
116    public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Encrypted_Failure()
117            throws Exception {
118        setupPassword();
119
120        try {
121            mGenerator.initialize(1024, new SecureRandom());
122            fail("KeyPairGenerator should not support setting the key size");
123        } catch (IllegalArgumentException success) {
124        }
125    }
126
127    public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Encrypted_Failure()
128            throws Exception {
129        setupPassword();
130
131        mGenerator.initialize(
132                new KeyPairGeneratorSpec.Builder(getContext())
133                        .setAlias(TEST_ALIAS_1)
134                        .setKeyType("RSA")
135                        .setKeySize(1024)
136                        .setSubject(TEST_DN_1)
137                        .setSerialNumber(TEST_SERIAL_1)
138                        .setStartDate(NOW)
139                        .setEndDate(NOW_PLUS_10_YEARS)
140                        .setEncryptionRequired()
141                        .build(),
142                new SecureRandom());
143    }
144
145    public void testKeyPairGenerator_GenerateKeyPair_Encrypted_Success() throws Exception {
146        setupPassword();
147
148        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
149                .setAlias(TEST_ALIAS_1)
150                .setSubject(TEST_DN_1)
151                .setSerialNumber(TEST_SERIAL_1)
152                .setStartDate(NOW)
153                .setEndDate(NOW_PLUS_10_YEARS)
154                .setEncryptionRequired()
155                .build());
156
157        final KeyPair pair = mGenerator.generateKeyPair();
158        assertNotNull("The KeyPair returned should not be null", pair);
159
160        assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
161                NOW_PLUS_10_YEARS);
162    }
163
164    public void testKeyPairGenerator_GenerateKeyPair_EC_Unencrypted_Success() throws Exception {
165        KeyPairGenerator generator = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
166        generator.initialize(new KeyGenParameterSpec.Builder(
167                TEST_ALIAS_1,
168                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
169                .setCertificateSubject(TEST_DN_1)
170                .setCertificateSerialNumber(TEST_SERIAL_1)
171                .setCertificateNotBefore(NOW)
172                .setCertificateNotAfter(NOW_PLUS_10_YEARS)
173                .setDigests(KeyProperties.DIGEST_SHA256)
174                .build());
175
176        final KeyPair pair = generator.generateKeyPair();
177        assertNotNull("The KeyPair returned should not be null", pair);
178
179        assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 256, null, TEST_DN_1, TEST_SERIAL_1, NOW,
180                NOW_PLUS_10_YEARS);
181    }
182
183    public void testKeyPairGenerator_Legacy_GenerateKeyPair_EC_Unencrypted_Success()
184            throws Exception {
185        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
186                .setAlias(TEST_ALIAS_1)
187                .setKeyType("EC")
188                .setSubject(TEST_DN_1)
189                .setSerialNumber(TEST_SERIAL_1)
190                .setStartDate(NOW)
191                .setEndDate(NOW_PLUS_10_YEARS)
192                .build());
193
194        final KeyPair pair = mGenerator.generateKeyPair();
195        assertNotNull("The KeyPair returned should not be null", pair);
196
197        assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 256, null, TEST_DN_1, TEST_SERIAL_1, NOW,
198                NOW_PLUS_10_YEARS);
199    }
200
201    public void testKeyPairGenerator_GenerateKeyPair_EC_P521_Unencrypted_Success() throws Exception {
202        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
203                .setAlias(TEST_ALIAS_1)
204                .setKeyType("EC")
205                .setKeySize(521)
206                .setSubject(TEST_DN_1)
207                .setSerialNumber(TEST_SERIAL_1)
208                .setStartDate(NOW)
209                .setEndDate(NOW_PLUS_10_YEARS)
210                .build());
211
212        final KeyPair pair = mGenerator.generateKeyPair();
213        assertNotNull("The KeyPair returned should not be null", pair);
214
215        assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 521, null, TEST_DN_1, TEST_SERIAL_1, NOW,
216                NOW_PLUS_10_YEARS);
217    }
218
219    public void testKeyPairGenerator_GenerateKeyPair_RSA_Unencrypted_Success() throws Exception {
220        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
221                .setAlias(TEST_ALIAS_1)
222                .setSubject(TEST_DN_1)
223                .setSerialNumber(TEST_SERIAL_1)
224                .setStartDate(NOW)
225                .setEndDate(NOW_PLUS_10_YEARS)
226                .build());
227
228        final KeyPair pair = mGenerator.generateKeyPair();
229        assertNotNull("The KeyPair returned should not be null", pair);
230
231        assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
232                NOW_PLUS_10_YEARS);
233    }
234
235    public void testKeyPairGenerator_GenerateKeyPair_RSA_WithParams_Unencrypted_Success()
236            throws Exception {
237        AlgorithmParameterSpec spec = new RSAKeyGenParameterSpec(1024, BigInteger.valueOf(3L));
238        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
239                .setAlias(TEST_ALIAS_1)
240                .setKeySize(1024)
241                .setAlgorithmParameterSpec(spec)
242                .setSubject(TEST_DN_1)
243                .setSerialNumber(TEST_SERIAL_1)
244                .setStartDate(NOW)
245                .setEndDate(NOW_PLUS_10_YEARS)
246                .build());
247
248        final KeyPair pair = mGenerator.generateKeyPair();
249        assertNotNull("The KeyPair returned should not be null", pair);
250
251        assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 1024, spec, TEST_DN_1, TEST_SERIAL_1, NOW,
252                NOW_PLUS_10_YEARS);
253    }
254
255    public void testKeyPairGenerator_GenerateKeyPair_Replaced_Success() throws Exception {
256        // Generate the first key
257        {
258            mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
259                    .setAlias(TEST_ALIAS_1)
260                    .setSubject(TEST_DN_1)
261                    .setSerialNumber(TEST_SERIAL_1)
262                    .setStartDate(NOW)
263                    .setEndDate(NOW_PLUS_10_YEARS)
264                    .build());
265            final KeyPair pair1 = mGenerator.generateKeyPair();
266            assertNotNull("The KeyPair returned should not be null", pair1);
267            assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1,
268                    NOW, NOW_PLUS_10_YEARS);
269        }
270
271        // Replace the original key
272        {
273            mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
274                    .setAlias(TEST_ALIAS_2)
275                    .setSubject(TEST_DN_2)
276                    .setSerialNumber(TEST_SERIAL_2)
277                    .setStartDate(NOW)
278                    .setEndDate(NOW_PLUS_10_YEARS)
279                    .build());
280            final KeyPair pair2 = mGenerator.generateKeyPair();
281            assertNotNull("The KeyPair returned should not be null", pair2);
282            assertKeyPairCorrect(pair2, TEST_ALIAS_2, "RSA", 2048, null, TEST_DN_2, TEST_SERIAL_2,
283                    NOW, NOW_PLUS_10_YEARS);
284        }
285    }
286
287    public void testKeyPairGenerator_GenerateKeyPair_Replaced_UnencryptedToEncrypted_Success()
288            throws Exception {
289        // Generate the first key
290        {
291            mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
292                    .setAlias(TEST_ALIAS_1)
293                    .setSubject(TEST_DN_1)
294                    .setSerialNumber(TEST_SERIAL_1)
295                    .setStartDate(NOW)
296                    .setEndDate(NOW_PLUS_10_YEARS)
297                    .build());
298            final KeyPair pair1 = mGenerator.generateKeyPair();
299            assertNotNull("The KeyPair returned should not be null", pair1);
300            assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1,
301                    NOW, NOW_PLUS_10_YEARS);
302        }
303
304        // Attempt to replace previous key
305        {
306            mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
307                    .setAlias(TEST_ALIAS_1)
308                    .setSubject(TEST_DN_2)
309                    .setSerialNumber(TEST_SERIAL_2)
310                    .setStartDate(NOW)
311                    .setEndDate(NOW_PLUS_10_YEARS)
312                    .setEncryptionRequired()
313                    .build());
314            try {
315                mGenerator.generateKeyPair();
316                fail("Should not be able to generate encrypted key while not initialized");
317            } catch (IllegalStateException expected) {
318            }
319
320            assertTrue(mAndroidKeyStore.onUserPasswordChanged("1111"));
321            assertTrue(mAndroidKeyStore.isUnlocked());
322
323            final KeyPair pair2 = mGenerator.generateKeyPair();
324            assertNotNull("The KeyPair returned should not be null", pair2);
325            assertKeyPairCorrect(pair2, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_2, TEST_SERIAL_2,
326                    NOW, NOW_PLUS_10_YEARS);
327        }
328    }
329
330    private void assertKeyPairCorrect(KeyPair pair, String alias, String keyType, int keySize,
331            AlgorithmParameterSpec spec, X500Principal dn, BigInteger serial, Date start, Date end)
332            throws Exception {
333        final PublicKey pubKey = pair.getPublic();
334        assertNotNull("The PublicKey for the KeyPair should be not null", pubKey);
335        assertEquals(keyType, pubKey.getAlgorithm());
336
337        if ("EC".equalsIgnoreCase(keyType)) {
338            assertEquals("Curve should be what was specified during initialization", keySize,
339                    ((ECPublicKey) pubKey).getParams().getCurve().getField().getFieldSize());
340        } else if ("RSA".equalsIgnoreCase(keyType)) {
341            RSAPublicKey rsaPubKey = (RSAPublicKey) pubKey;
342            assertEquals("Modulus size should be what is specified during initialization",
343                    (keySize + 7) & ~7, (rsaPubKey.getModulus().bitLength() + 7) & ~7);
344            if (spec != null) {
345                RSAKeyGenParameterSpec params = (RSAKeyGenParameterSpec) spec;
346                assertEquals((keySize + 7) & ~7, (params.getKeysize() + 7) & ~7);
347                assertEquals(params.getPublicExponent(), rsaPubKey.getPublicExponent());
348            }
349        }
350
351        final PrivateKey privKey = pair.getPrivate();
352        assertNotNull("The PrivateKey for the KeyPair should be not null", privKey);
353        assertEquals(keyType, privKey.getAlgorithm());
354
355        if ("EC".equalsIgnoreCase(keyType)) {
356            assertTrue("EC private key must be instanceof ECKey: " + privKey.getClass().getName(),
357                    privKey instanceof ECKey);
358            assertEquals("Private and public key must have the same EC parameters",
359                    ((ECKey) pubKey).getParams(), ((ECKey) privKey).getParams());
360        } else if ("RSA".equalsIgnoreCase(keyType)) {
361            assertTrue("RSA private key must be instance of RSAKey: "
362                    + privKey.getClass().getName(),
363                    privKey instanceof RSAKey);
364            assertEquals("Private and public key must have the same RSA modulus",
365                    ((RSAKey) pubKey).getModulus(), ((RSAKey) privKey).getModulus());
366        }
367
368        final byte[] userCertBytes = mAndroidKeyStore.get(Credentials.USER_CERTIFICATE + alias);
369        assertNotNull("The user certificate should exist for the generated entry", userCertBytes);
370
371        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
372        final Certificate userCert =
373                cf.generateCertificate(new ByteArrayInputStream(userCertBytes));
374
375        assertTrue("Certificate should be in X.509 format", userCert instanceof X509Certificate);
376
377        final X509Certificate x509userCert = (X509Certificate) userCert;
378
379        assertEquals(
380                "Public key used to sign certificate should have the same algorithm as in KeyPair",
381                pubKey.getAlgorithm(), x509userCert.getPublicKey().getAlgorithm());
382
383        assertEquals("PublicKey used to sign certificate should match one returned in KeyPair",
384                pubKey,
385                AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
386                        Credentials.USER_PRIVATE_KEY + alias,
387                        KeyStore.UID_SELF,
388                        x509userCert.getPublicKey().getAlgorithm(),
389                        x509userCert.getPublicKey().getEncoded()));
390
391        assertEquals("The Subject DN should be the one passed into the params", dn,
392                x509userCert.getSubjectDN());
393
394        assertEquals("The Issuer DN should be the same as the Subject DN", dn,
395                x509userCert.getIssuerDN());
396
397        assertEquals("The Serial should be the one passed into the params", serial,
398                x509userCert.getSerialNumber());
399
400        assertDateEquals("The notBefore date should be the one passed into the params", start,
401                x509userCert.getNotBefore());
402
403        assertDateEquals("The notAfter date should be the one passed into the params", end,
404                x509userCert.getNotAfter());
405
406        // Assert that the cert's signature verifies using the public key from generated KeyPair
407        x509userCert.verify(pubKey);
408        // Assert that the cert's signature verifies using the public key from the cert itself.
409        x509userCert.verify(x509userCert.getPublicKey());
410
411        final byte[] caCerts = mAndroidKeyStore.get(Credentials.CA_CERTIFICATE + alias);
412        assertNull("A list of CA certificates should not exist for the generated entry", caCerts);
413
414        ExportResult exportResult = mAndroidKeyStore.exportKey(
415                Credentials.USER_PRIVATE_KEY + alias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null);
416        assertEquals(KeyStore.NO_ERROR, exportResult.resultCode);
417        final byte[] pubKeyBytes = exportResult.exportData;
418        assertNotNull("The keystore should return the public key for the generated key",
419                pubKeyBytes);
420        assertTrue("Public key X.509 format should be as expected",
421                Arrays.equals(pubKey.getEncoded(), pubKeyBytes));
422    }
423
424    private static void assertDateEquals(String message, Date date1, Date date2) throws Exception {
425        SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
426
427        String result1 = formatter.format(date1);
428        String result2 = formatter.format(date2);
429
430        assertEquals(message, result1, result2);
431    }
432}
433