1/*
2 * Copyright (C) 2010 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 libcore.javax.net.ssl;
18
19import java.security.InvalidAlgorithmParameterException;
20import java.security.KeyStore.Builder;
21import java.security.KeyStore.PasswordProtection;
22import java.security.KeyStore.PrivateKeyEntry;
23import java.security.PrivateKey;
24import java.security.Provider;
25import java.security.Security;
26import java.security.cert.Certificate;
27import java.security.cert.X509Certificate;
28import java.util.Arrays;
29import java.util.Locale;
30import java.util.Set;
31import javax.net.ssl.KeyManager;
32import javax.net.ssl.KeyManagerFactory;
33import javax.net.ssl.KeyStoreBuilderParameters;
34import javax.net.ssl.ManagerFactoryParameters;
35import javax.net.ssl.X509ExtendedKeyManager;
36import javax.net.ssl.X509KeyManager;
37import junit.framework.TestCase;
38import libcore.java.security.StandardNames;
39import libcore.java.security.TestKeyStore;
40
41public class KeyManagerFactoryTest extends TestCase {
42
43    private TestKeyStore testKeyStore;
44
45    protected void setUp() throws Exception {
46        // note the rare usage of DSA keys here in addition to RSA
47        testKeyStore = new TestKeyStore.Builder()
48                .keyAlgorithms("RSA", "DH_RSA", "DSA", "DH_DSA", "EC", "EC_RSA")
49                .aliasPrefix("rsa-dsa-ec-dh")
50                .build();
51    }
52
53    private TestKeyStore getTestKeyStore() throws Exception {
54        return testKeyStore;
55    }
56
57    public void test_KeyManagerFactory_getDefaultAlgorithm() throws Exception {
58        String algorithm = KeyManagerFactory.getDefaultAlgorithm();
59        assertEquals(StandardNames.KEY_MANAGER_FACTORY_DEFAULT, algorithm);
60        KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
61        test_KeyManagerFactory(kmf);
62    }
63
64    private static class UselessManagerFactoryParameters implements ManagerFactoryParameters {}
65
66    private static boolean supportsManagerFactoryParameters(String algorithm) {
67        // Only the "New" one supports ManagerFactoryParameters
68        return algorithm.equals("NewSunX509");
69    }
70
71    private static String[] keyTypes(String algorithm) {
72        // Although the "New" one supports ManagerFactoryParameters,
73        // it can't handle nulls in the key types array.
74        return (algorithm.equals("NewSunX509")
75                ? KEY_TYPES_WITH_EMPTY
76                : KEY_TYPES_WITH_EMPTY_AND_NULL);
77    }
78
79    private void test_KeyManagerFactory(KeyManagerFactory kmf) throws Exception {
80        assertNotNull(kmf);
81        assertNotNull(kmf.getAlgorithm());
82        assertNotNull(kmf.getProvider());
83
84        // before init
85        try {
86            kmf.getKeyManagers();
87            fail();
88        } catch (IllegalStateException expected) {
89        }
90
91        // init with null ManagerFactoryParameters
92        try {
93            kmf.init(null);
94            fail();
95        } catch (InvalidAlgorithmParameterException expected) {
96        }
97
98        // init with useless ManagerFactoryParameters
99        try {
100            kmf.init(new UselessManagerFactoryParameters());
101            fail();
102        } catch (InvalidAlgorithmParameterException expected) {
103        }
104
105        // init with KeyStoreBuilderParameters ManagerFactoryParameters
106        PasswordProtection pp = new PasswordProtection(getTestKeyStore().storePassword);
107        Builder builder = Builder.newInstance(getTestKeyStore().keyStore, pp);
108        KeyStoreBuilderParameters ksbp = new KeyStoreBuilderParameters(builder);
109        if (supportsManagerFactoryParameters(kmf.getAlgorithm())) {
110            kmf.init(ksbp);
111            test_KeyManagerFactory_getKeyManagers(kmf, false);
112        } else {
113            try {
114                kmf.init(ksbp);
115                fail();
116            } catch (InvalidAlgorithmParameterException expected) {
117            }
118        }
119
120        // init with null for default behavior
121        kmf.init(null, null);
122        test_KeyManagerFactory_getKeyManagers(kmf, true);
123
124        // init with specific key store and password
125        kmf.init(getTestKeyStore().keyStore, getTestKeyStore().storePassword);
126        test_KeyManagerFactory_getKeyManagers(kmf, false);
127    }
128
129    private void test_KeyManagerFactory_getKeyManagers(KeyManagerFactory kmf, boolean empty)
130            throws Exception {
131        KeyManager[] keyManagers = kmf.getKeyManagers();
132        assertNotNull(keyManagers);
133        assertTrue(keyManagers.length > 0);
134        for (KeyManager keyManager : keyManagers) {
135            assertNotNull(keyManager);
136            if (keyManager instanceof X509KeyManager) {
137                test_X509KeyManager((X509KeyManager) keyManager, empty, kmf.getAlgorithm());
138            }
139        }
140    }
141
142    private static final String[] KEY_TYPES_ONLY
143            = StandardNames.KEY_TYPES.toArray(new String[StandardNames.KEY_TYPES.size()]);
144    private static final String[] KEY_TYPES_WITH_EMPTY
145            = new String[KEY_TYPES_ONLY.length + 1];
146    private static final String[] KEY_TYPES_WITH_EMPTY_AND_NULL
147            = new String[KEY_TYPES_ONLY.length + 2];
148    static {
149        System.arraycopy(KEY_TYPES_ONLY, 0,
150                         KEY_TYPES_WITH_EMPTY, 0,
151                         KEY_TYPES_ONLY.length);
152        KEY_TYPES_WITH_EMPTY[KEY_TYPES_WITH_EMPTY.length-1] = "";
153
154        System.arraycopy(KEY_TYPES_WITH_EMPTY, 0,
155                         KEY_TYPES_WITH_EMPTY_AND_NULL, 0,
156                         KEY_TYPES_WITH_EMPTY.length);
157        // extra null at end requires no initialization
158    }
159
160    private void test_X509KeyManager(X509KeyManager km, boolean empty, String algorithm)
161            throws Exception {
162        String[] keyTypes = keyTypes(algorithm);
163        for (String keyType : keyTypes) {
164            String[] aliases = km.getClientAliases(keyType, null);
165            if (empty || keyType == null || keyType.isEmpty()) {
166                assertNull(keyType, aliases);
167                continue;
168            }
169            assertNotNull(keyType, aliases);
170            for (String alias : aliases) {
171                test_X509KeyManager_alias(km, alias, keyType, false, empty);
172            }
173        }
174        for (String keyType : keyTypes) {
175            String[] aliases = km.getServerAliases(keyType, null);
176            if (empty || keyType == null || keyType.isEmpty()) {
177                assertNull(keyType, aliases);
178                continue;
179            }
180            assertNotNull(keyType, aliases);
181            for (String alias : aliases) {
182                test_X509KeyManager_alias(km, alias, keyType, false, empty);
183            }
184        }
185
186        String a = km.chooseClientAlias(keyTypes, null, null);
187        test_X509KeyManager_alias(km, a, null, true, empty);
188
189        for (String keyType : keyTypes) {
190            String[] array = new String[] { keyType };
191            String alias = km.chooseClientAlias(array, null, null);
192            test_X509KeyManager_alias(km, alias, keyType, false, empty);
193        }
194        for (String keyType : keyTypes) {
195            String alias = km.chooseServerAlias(keyType, null, null);
196            test_X509KeyManager_alias(km, alias, keyType, false, empty);
197        }
198        if (km instanceof X509ExtendedKeyManager) {
199            test_X509ExtendedKeyManager((X509ExtendedKeyManager) km, empty, algorithm);
200        }
201    }
202
203    private void test_X509ExtendedKeyManager(X509ExtendedKeyManager km,
204                                             boolean empty, String algorithm) throws Exception {
205        String[] keyTypes = keyTypes(algorithm);
206        String a = km.chooseEngineClientAlias(keyTypes, null, null);
207        test_X509KeyManager_alias(km, a, null, true, empty);
208        for (String keyType : keyTypes) {
209            String[] array = new String[] { keyType };
210            String alias = km.chooseEngineClientAlias(array, null, null);
211            test_X509KeyManager_alias(km, alias, keyType, false, empty);
212        }
213        for (String keyType : keyTypes) {
214            String alias = km.chooseEngineServerAlias(keyType, null, null);
215            test_X509KeyManager_alias(km, alias, keyType, false, empty);
216        }
217    }
218
219    private void test_X509KeyManager_alias(X509KeyManager km,
220                                           String alias,
221                                           String keyType,
222                                           boolean many,
223                                           boolean empty) throws Exception {
224        if (empty || (!many && (keyType == null || keyType.isEmpty()))) {
225            assertNull(keyType, alias);
226            assertNull(keyType, km.getCertificateChain(alias));
227            assertNull(keyType, km.getPrivateKey(alias));
228            return;
229        }
230        assertNotNull(keyType, alias);
231
232        X509Certificate[] certificateChain = km.getCertificateChain(alias);
233        PrivateKey privateKey = km.getPrivateKey(alias);
234
235        String keyAlgName =  privateKey.getAlgorithm();
236
237        X509Certificate certificate = certificateChain[0];
238        assertEquals(keyType, keyAlgName, certificate.getPublicKey().getAlgorithm());
239
240        String sigAlgName = certificate.getSigAlgName();
241
242        PrivateKeyEntry privateKeyEntry = getTestKeyStore().getPrivateKey(keyAlgName, sigAlgName);
243
244        assertEquals(keyType,
245                     Arrays.<Certificate>asList(privateKeyEntry.getCertificateChain()),
246                     Arrays.<Certificate>asList(certificateChain));
247        assertEquals(keyType,
248                     privateKeyEntry.getPrivateKey(), privateKey);
249
250        if (keyType != null) {
251            assertEquals(TestKeyStore.keyAlgorithm(keyType), keyAlgName);
252
253            // Skip this when we're given only "DH" or "EC" instead of "DH_DSA",
254            // "EC_RSA", etc. since we don't know what the expected
255            // algorithm was.
256            if (!keyType.equals("DH") && !keyType.equals("EC")) {
257                assertTrue(sigAlgName.contains(TestKeyStore.signatureAlgorithm(keyType)));
258            }
259        }
260    }
261
262    public void test_KeyManagerFactory_getInstance() throws Exception {
263        Provider[] providers = Security.getProviders();
264        for (Provider provider : providers) {
265            Set<Provider.Service> services = provider.getServices();
266            for (Provider.Service service : services) {
267                String type = service.getType();
268                if (!type.equals("KeyManagerFactory")) {
269                    continue;
270                }
271                String algorithm = service.getAlgorithm();
272                try {
273                    {
274                        KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
275                        assertEquals(algorithm, kmf.getAlgorithm());
276                        test_KeyManagerFactory(kmf);
277                    }
278
279                    {
280                        KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm,
281                                                                              provider);
282                        assertEquals(algorithm, kmf.getAlgorithm());
283                        assertEquals(provider, kmf.getProvider());
284                        test_KeyManagerFactory(kmf);
285                    }
286
287                    {
288                        KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm,
289                                                                              provider.getName());
290                        assertEquals(algorithm, kmf.getAlgorithm());
291                        assertEquals(provider, kmf.getProvider());
292                        test_KeyManagerFactory(kmf);
293                    }
294                } catch (Exception e) {
295                    throw new Exception("Problem with algorithm " + algorithm, e);
296                }
297            }
298        }
299    }
300}
301