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.io.IOException;
20import java.net.InetAddress;
21import java.net.Socket;
22import java.net.UnknownHostException;
23import java.security.KeyStore;
24import java.security.Principal;
25import java.security.SecureRandom;
26import java.security.cert.Certificate;
27import java.security.cert.CertificateException;
28import java.security.cert.X509Certificate;
29import java.util.Collections;
30import javax.net.ssl.KeyManager;
31import javax.net.ssl.SSLContext;
32import javax.net.ssl.SSLServerSocket;
33import javax.net.ssl.SSLSocket;
34import javax.net.ssl.SSLSocketFactory;
35import javax.net.ssl.TrustManager;
36import javax.net.ssl.X509ExtendedKeyManager;
37import javax.net.ssl.X509TrustManager;
38import junit.framework.Assert;
39import libcore.java.security.StandardNames;
40import libcore.java.security.TestKeyStore;
41
42/**
43 * TestSSLContext is a convenience class for other tests that
44 * want a canned SSLContext and related state for testing so they
45 * don't have to duplicate the logic.
46 */
47public final class TestSSLContext extends Assert {
48
49    /*
50     * The RI and Android have very different default SSLSession cache behaviors.
51     * The RI keeps an unlimited number of SSLSesions around for 1 day.
52     * Android keeps 10 SSLSessions forever.
53     */
54    private static final boolean IS_RI = StandardNames.IS_RI;
55    public static final int EXPECTED_DEFAULT_CLIENT_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 10;
56    public static final int EXPECTED_DEFAULT_SERVER_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 100;
57    public static final int EXPECTED_DEFAULT_SSL_SESSION_CACHE_TIMEOUT = (IS_RI) ? 86400 : 0;
58
59    /**
60     * The Android SSLSocket and SSLServerSocket implementations are
61     * based on a version of OpenSSL which includes support for RFC
62     * 4507 session tickets. When using session tickets, the server
63     * does not need to keep a cache mapping session IDs to SSL
64     * sessions for reuse. Instead, the client presents the server
65     * with a session ticket it received from the server earlier,
66     * which is an SSL session encrypted by the server's secret
67     * key. Since in this case the server does not need to keep a
68     * cache, some tests may find different results depending on
69     * whether or not the session tickets are in use. These tests can
70     * use this function to determine if loopback SSL connections are
71     * expected to use session tickets and conditionalize their
72     * results appropriately.
73     */
74    public static boolean sslServerSocketSupportsSessionTickets () {
75        // Disabled session tickets for better compatability b/2682876
76        // return !IS_RI;
77        return false;
78    }
79
80    public final KeyStore clientKeyStore;
81    public final char[] clientStorePassword;
82    public final KeyStore serverKeyStore;
83    public final char[] serverStorePassword;
84    public final X509ExtendedKeyManager clientKeyManager;
85    public final X509ExtendedKeyManager serverKeyManager;
86    public final X509TrustManager clientTrustManager;
87    public final X509TrustManager serverTrustManager;
88    public final SSLContext clientContext;
89    public final SSLContext serverContext;
90    public final SSLServerSocket serverSocket;
91    public final InetAddress host;
92    public final int port;
93
94    private TestSSLContext(KeyStore clientKeyStore,
95                           char[] clientStorePassword,
96                           KeyStore serverKeyStore,
97                           char[] serverStorePassword,
98                           X509ExtendedKeyManager clientKeyManager,
99                           X509ExtendedKeyManager serverKeyManager,
100                           X509TrustManager clientTrustManager,
101                           X509TrustManager serverTrustManager,
102                           SSLContext clientContext,
103                           SSLContext serverContext,
104                           SSLServerSocket serverSocket,
105                           InetAddress host,
106                           int port) {
107        this.clientKeyStore = clientKeyStore;
108        this.clientStorePassword = clientStorePassword;
109        this.serverKeyStore = serverKeyStore;
110        this.serverStorePassword = serverStorePassword;
111        this.clientKeyManager = clientKeyManager;
112        this.serverKeyManager = serverKeyManager;
113        this.clientTrustManager = clientTrustManager;
114        this.serverTrustManager = serverTrustManager;
115        this.clientContext = clientContext;
116        this.serverContext = serverContext;
117        this.serverSocket = serverSocket;
118        this.host = host;
119        this.port = port;
120    }
121
122    public void close() {
123        try {
124            serverSocket.close();
125        } catch (Exception e) {
126            throw new RuntimeException(e);
127        }
128    }
129
130    /**
131     * Usual TestSSLContext creation method, creates underlying
132     * SSLContext with certificate and key as well as SSLServerSocket
133     * listening provided host and port.
134     */
135    public static TestSSLContext create() {
136        return create(TestKeyStore.getClient(),
137                      TestKeyStore.getServer());
138    }
139
140    /**
141     * TestSSLContext creation method that allows separate creation of server key store
142     */
143    public static TestSSLContext create(TestKeyStore client, TestKeyStore server) {
144        String provider = StandardNames.JSSE_PROVIDER_NAME;
145        return create(client, server, provider, provider);
146    }
147    public static TestSSLContext create(TestKeyStore client, TestKeyStore server,
148                                        String clientProvider, String serverProvider) {
149        String protocol = "TLS";
150        SSLContext clientContext = createSSLContext(protocol, clientProvider,
151                                                    client.keyManagers, client.trustManagers);
152        SSLContext serverContext = createSSLContext(protocol, serverProvider,
153                                                    server.keyManagers, server.trustManagers);
154        return create(client.keyStore, client.storePassword,
155                      server.keyStore, server.storePassword,
156                      client.keyManagers[0],
157                      server.keyManagers[0],
158                      client.trustManagers[0],
159                      server.trustManagers[0],
160                      clientContext,
161                      serverContext);
162    }
163
164    /**
165     * TestSSLContext creation method that allows separate creation of client and server key store
166     */
167    public static TestSSLContext create(KeyStore clientKeyStore, char[] clientStorePassword,
168                                        KeyStore serverKeyStore, char[] serverStorePassword,
169                                        KeyManager clientKeyManagers,
170                                        KeyManager serverKeyManagers,
171                                        TrustManager clientTrustManagers,
172                                        TrustManager serverTrustManagers,
173                                        SSLContext clientContext,
174                                        SSLContext serverContext) {
175        try {
176            SSLServerSocket serverSocket = (SSLServerSocket)
177                serverContext.getServerSocketFactory().createServerSocket(0);
178            InetAddress host = InetAddress.getLocalHost();
179            int port = serverSocket.getLocalPort();
180
181            return new TestSSLContext(clientKeyStore, clientStorePassword,
182                                      serverKeyStore, serverStorePassword,
183                                      (X509ExtendedKeyManager) clientKeyManagers,
184                                      (X509ExtendedKeyManager) serverKeyManagers,
185                                      (X509TrustManager) clientTrustManagers,
186                                      (X509TrustManager) serverTrustManagers,
187                                      clientContext, serverContext,
188                                      serverSocket, host, port);
189        } catch (RuntimeException e) {
190            throw e;
191        } catch (Exception e) {
192            throw new RuntimeException(e);
193        }
194    }
195
196    /**
197     * Create a SSLContext with a KeyManager using the private key and
198     * certificate chain from the given KeyStore and a TrustManager
199     * using the certificates authorities from the same KeyStore.
200     */
201    public static final SSLContext createSSLContext(final String protocol,
202                                                    final String provider,
203                                                    final KeyManager[] keyManagers,
204                                                    final TrustManager[] trustManagers)
205    {
206        try {
207            SSLContext context = SSLContext.getInstance(protocol, provider);
208            context.init(keyManagers, trustManagers, new SecureRandom());
209            return context;
210        } catch (Exception e) {
211            throw new RuntimeException(e);
212        }
213    }
214
215    public static void assertCertificateInKeyStore(Principal principal,
216                                                   KeyStore keyStore) throws Exception {
217        String subjectName = principal.getName();
218        boolean found = false;
219        for (String alias: Collections.list(keyStore.aliases())) {
220            if (!keyStore.isCertificateEntry(alias)) {
221                continue;
222            }
223            X509Certificate keyStoreCertificate = (X509Certificate) keyStore.getCertificate(alias);
224            if (subjectName.equals(keyStoreCertificate.getSubjectDN().getName())) {
225                found = true;
226                break;
227            }
228        }
229        assertTrue(found);
230    }
231
232    public static void assertCertificateInKeyStore(Certificate certificate,
233                                                   KeyStore keyStore) throws Exception {
234        boolean found = false;
235        for (String alias: Collections.list(keyStore.aliases())) {
236            if (!keyStore.isCertificateEntry(alias)) {
237                continue;
238            }
239            Certificate keyStoreCertificate = keyStore.getCertificate(alias);
240            if (certificate.equals(keyStoreCertificate)) {
241                found = true;
242                break;
243            }
244        }
245        assertTrue(found);
246    }
247
248    public static void assertServerCertificateChain(X509TrustManager trustManager,
249                                                    Certificate[] serverChain)
250            throws CertificateException {
251        X509Certificate[] chain = (X509Certificate[]) serverChain;
252        trustManager.checkServerTrusted(chain, chain[0].getPublicKey().getAlgorithm());
253    }
254
255    public static void assertClientCertificateChain(X509TrustManager trustManager,
256                                                    Certificate[] clientChain)
257            throws CertificateException {
258        X509Certificate[] chain = (X509Certificate[]) clientChain;
259        trustManager.checkClientTrusted(chain, chain[0].getPublicKey().getAlgorithm());
260    }
261
262    /**
263     * Returns an SSLSocketFactory that calls setWantClientAuth and
264     * setNeedClientAuth as specified on all returned sockets.
265     */
266    public static SSLSocketFactory clientAuth(final SSLSocketFactory sf,
267                                              final boolean want,
268                                              final boolean need) {
269        return new SSLSocketFactory() {
270            private SSLSocket set(Socket socket) {
271                SSLSocket s = (SSLSocket) socket;
272                s.setWantClientAuth(want);
273                s.setNeedClientAuth(need);
274                return s;
275            }
276            public Socket createSocket(String host, int port)
277                    throws IOException, UnknownHostException {
278                return set(sf.createSocket(host, port));
279            }
280            public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
281                    throws IOException, UnknownHostException {
282                return set(sf.createSocket(host, port, localHost, localPort));
283            }
284            public Socket createSocket(InetAddress host, int port) throws IOException {
285                return set(sf.createSocket(host, port));
286            }
287            public Socket createSocket(InetAddress address, int port,
288                                       InetAddress localAddress, int localPort) throws IOException {
289                return set(sf.createSocket(address, port));
290            }
291
292            public String[] getDefaultCipherSuites() {
293                return sf.getDefaultCipherSuites();
294            }
295            public String[] getSupportedCipherSuites() {
296                return sf.getSupportedCipherSuites();
297            }
298
299            public Socket createSocket(Socket s, String host, int port, boolean autoClose)
300                    throws IOException {
301                return set(sf.createSocket(s, host, port, autoClose));
302            }
303        };
304    }
305}
306