AndroidKeyPairGeneratorTest.java revision 2eeda7286f3c7cb79f7eb71ae6464cad213d12a3
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;
18
19import android.test.AndroidTestCase;
20
21import java.io.ByteArrayInputStream;
22import java.math.BigInteger;
23import java.security.KeyPair;
24import java.security.PrivateKey;
25import java.security.PublicKey;
26import java.security.SecureRandom;
27import java.security.cert.Certificate;
28import java.security.cert.CertificateFactory;
29import java.security.cert.X509Certificate;
30import java.text.SimpleDateFormat;
31import java.util.Date;
32
33import javax.security.auth.x500.X500Principal;
34
35public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
36    private android.security.KeyStore mAndroidKeyStore;
37
38    private java.security.KeyPairGenerator mGenerator;
39
40    private static final String TEST_ALIAS_1 = "test1";
41
42    private static final String TEST_ALIAS_2 = "test2";
43
44    private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
45
46    private static final X500Principal TEST_DN_2 = new X500Principal("CN=test2");
47
48    private static final BigInteger TEST_SERIAL_1 = BigInteger.ONE;
49
50    private static final BigInteger TEST_SERIAL_2 = BigInteger.valueOf(2L);
51
52    private static final long NOW_MILLIS = System.currentTimeMillis();
53
54    /* We have to round this off because X509v3 doesn't store milliseconds. */
55    private static final Date NOW = new Date(NOW_MILLIS - (NOW_MILLIS % 1000L));
56
57    @SuppressWarnings("deprecation")
58    private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
59
60    @Override
61    protected void setUp() throws Exception {
62        mAndroidKeyStore = android.security.KeyStore.getInstance();
63
64        assertTrue(mAndroidKeyStore.reset());
65
66        assertFalse(mAndroidKeyStore.isUnlocked());
67
68        mGenerator = java.security.KeyPairGenerator.getInstance("AndroidKeyStore");
69    }
70
71    private void setupPassword() {
72        assertTrue(mAndroidKeyStore.password("1111"));
73        assertTrue(mAndroidKeyStore.isUnlocked());
74
75        String[] aliases = mAndroidKeyStore.saw("");
76        assertNotNull(aliases);
77        assertEquals(0, aliases.length);
78    }
79
80    public void testKeyPairGenerator_Initialize_Params_Encrypted_Success() throws Exception {
81        setupPassword();
82
83        mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
84                .setAlias(TEST_ALIAS_1)
85                .setSubject(TEST_DN_1)
86                .setSerialNumber(TEST_SERIAL_1)
87                .setStartDate(NOW)
88                .setEndDate(NOW_PLUS_10_YEARS)
89                .setEncryptionRequired()
90                .build());
91    }
92
93    public void testKeyPairGenerator_Initialize_KeySize_Encrypted_Failure() throws Exception {
94        setupPassword();
95
96        try {
97            mGenerator.initialize(1024);
98            fail("KeyPairGenerator should not support setting the key size");
99        } catch (IllegalArgumentException success) {
100        }
101    }
102
103    public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Encrypted_Failure()
104            throws Exception {
105        setupPassword();
106
107        try {
108            mGenerator.initialize(1024, new SecureRandom());
109            fail("KeyPairGenerator should not support setting the key size");
110        } catch (IllegalArgumentException success) {
111        }
112    }
113
114    public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Encrypted_Failure()
115            throws Exception {
116        setupPassword();
117
118        mGenerator.initialize(
119                new AndroidKeyPairGeneratorSpec.Builder(getContext())
120                        .setAlias(TEST_ALIAS_1)
121                        .setSubject(TEST_DN_1)
122                        .setSerialNumber(TEST_SERIAL_1)
123                        .setStartDate(NOW)
124                        .setEndDate(NOW_PLUS_10_YEARS)
125                        .setEncryptionRequired()
126                        .build(),
127                new SecureRandom());
128    }
129
130    public void testKeyPairGenerator_GenerateKeyPair_Encrypted_Success() throws Exception {
131        setupPassword();
132
133        mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
134                .setAlias(TEST_ALIAS_1)
135                .setSubject(TEST_DN_1)
136                .setSerialNumber(TEST_SERIAL_1)
137                .setStartDate(NOW)
138                .setEndDate(NOW_PLUS_10_YEARS)
139                .setEncryptionRequired()
140                .build());
141
142        final KeyPair pair = mGenerator.generateKeyPair();
143        assertNotNull("The KeyPair returned should not be null", pair);
144
145        assertKeyPairCorrect(pair, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS);
146    }
147
148    public void testKeyPairGenerator_GenerateKeyPair_Unencrypted_Success() throws Exception {
149        mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
150                .setAlias(TEST_ALIAS_1)
151                .setSubject(TEST_DN_1)
152                .setSerialNumber(TEST_SERIAL_1)
153                .setStartDate(NOW)
154                .setEndDate(NOW_PLUS_10_YEARS)
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, TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS);
161    }
162
163    public void testKeyPairGenerator_GenerateKeyPair_Replaced_Success() throws Exception {
164        // Generate the first key
165        {
166            mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
167                    .setAlias(TEST_ALIAS_1)
168                    .setSubject(TEST_DN_1)
169                    .setSerialNumber(TEST_SERIAL_1)
170                    .setStartDate(NOW)
171                    .setEndDate(NOW_PLUS_10_YEARS)
172                    .build());
173            final KeyPair pair1 = mGenerator.generateKeyPair();
174            assertNotNull("The KeyPair returned should not be null", pair1);
175            assertKeyPairCorrect(pair1, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW,
176                    NOW_PLUS_10_YEARS);
177        }
178
179        // Replace the original key
180        {
181            mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
182                    .setAlias(TEST_ALIAS_2)
183                    .setSubject(TEST_DN_2)
184                    .setSerialNumber(TEST_SERIAL_2)
185                    .setStartDate(NOW)
186                    .setEndDate(NOW_PLUS_10_YEARS)
187                    .build());
188            final KeyPair pair2 = mGenerator.generateKeyPair();
189            assertNotNull("The KeyPair returned should not be null", pair2);
190            assertKeyPairCorrect(pair2, TEST_ALIAS_2, TEST_DN_2, TEST_SERIAL_2, NOW,
191                    NOW_PLUS_10_YEARS);
192        }
193    }
194
195    public void testKeyPairGenerator_GenerateKeyPair_Replaced_UnencryptedToEncrypted_Success()
196            throws Exception {
197        // Generate the first key
198        {
199            mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
200                    .setAlias(TEST_ALIAS_1)
201                    .setSubject(TEST_DN_1)
202                    .setSerialNumber(TEST_SERIAL_1)
203                    .setStartDate(NOW)
204                    .setEndDate(NOW_PLUS_10_YEARS)
205                    .build());
206            final KeyPair pair1 = mGenerator.generateKeyPair();
207            assertNotNull("The KeyPair returned should not be null", pair1);
208            assertKeyPairCorrect(pair1, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW,
209                    NOW_PLUS_10_YEARS);
210        }
211
212        // Attempt to replace previous key
213        {
214            mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
215                    .setAlias(TEST_ALIAS_1)
216                    .setSubject(TEST_DN_2)
217                    .setSerialNumber(TEST_SERIAL_2)
218                    .setStartDate(NOW)
219                    .setEndDate(NOW_PLUS_10_YEARS)
220                    .setEncryptionRequired()
221                    .build());
222            try {
223                mGenerator.generateKeyPair();
224                fail("Should not be able to generate encrypted key while not initialized");
225            } catch (IllegalStateException expected) {
226            }
227
228            assertTrue(mAndroidKeyStore.password("1111"));
229            assertTrue(mAndroidKeyStore.isUnlocked());
230
231            final KeyPair pair2 = mGenerator.generateKeyPair();
232            assertNotNull("The KeyPair returned should not be null", pair2);
233            assertKeyPairCorrect(pair2, TEST_ALIAS_1, TEST_DN_2, TEST_SERIAL_2, NOW,
234                    NOW_PLUS_10_YEARS);
235        }
236    }
237
238    private void assertKeyPairCorrect(KeyPair pair, String alias, X500Principal dn,
239            BigInteger serial, Date start, Date end) throws Exception {
240        final PublicKey pubKey = pair.getPublic();
241        assertNotNull("The PublicKey for the KeyPair should be not null", pubKey);
242
243        final PrivateKey privKey = pair.getPrivate();
244        assertNotNull("The PrivateKey for the KeyPair should be not null", privKey);
245
246        final byte[] userCertBytes = mAndroidKeyStore.get(Credentials.USER_CERTIFICATE + alias);
247        assertNotNull("The user certificate should exist for the generated entry", userCertBytes);
248
249        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
250        final Certificate userCert = cf
251                .generateCertificate(new ByteArrayInputStream(userCertBytes));
252
253        assertTrue("Certificate should be in X.509 format", userCert instanceof X509Certificate);
254
255        final X509Certificate x509userCert = (X509Certificate) userCert;
256
257        assertEquals("PublicKey used to sign certificate should match one returned in KeyPair",
258                pubKey, x509userCert.getPublicKey());
259
260        assertEquals("The Subject DN should be the one passed into the params", dn,
261                x509userCert.getSubjectDN());
262
263        assertEquals("The Issuer DN should be the same as the Subject DN", dn,
264                x509userCert.getIssuerDN());
265
266        assertEquals("The Serial should be the one passed into the params", serial,
267                x509userCert.getSerialNumber());
268
269        assertDateEquals("The notBefore date should be the one passed into the params", start,
270                x509userCert.getNotBefore());
271
272        assertDateEquals("The notAfter date should be the one passed into the params", end,
273                x509userCert.getNotAfter());
274
275        x509userCert.verify(pubKey);
276
277        final byte[] caCerts = mAndroidKeyStore.get(Credentials.CA_CERTIFICATE + alias);
278        assertNull("A list of CA certificates should not exist for the generated entry", caCerts);
279
280        final byte[] pubKeyBytes = mAndroidKeyStore.getPubkey(Credentials.USER_PRIVATE_KEY + alias);
281        assertNotNull("The keystore should return the public key for the generated key",
282                pubKeyBytes);
283    }
284
285    private static void assertDateEquals(String message, Date date1, Date date2) throws Exception {
286        SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
287
288        String result1 = formatter.format(date1);
289        String result2 = formatter.format(date2);
290
291        assertEquals(message, result1, result2);
292    }
293}
294