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.ByteArrayInputStream;
20import java.io.ByteArrayOutputStream;
21import java.io.IOException;
22import java.io.ObjectInputStream;
23import java.io.ObjectOutput;
24import java.io.ObjectOutputStream;
25import java.io.OutputStream;
26import java.net.InetAddress;
27import java.net.InetSocketAddress;
28import java.net.Socket;
29import java.net.UnknownHostException;
30import java.security.KeyStore;
31import java.security.Principal;
32import java.security.SecureRandom;
33import java.security.cert.Certificate;
34import java.security.cert.CertificateException;
35import java.security.cert.X509Certificate;
36import java.util.Collections;
37import javax.net.ssl.KeyManager;
38import javax.net.ssl.SSLContext;
39import javax.net.ssl.SSLServerSocket;
40import javax.net.ssl.SSLSocket;
41import javax.net.ssl.SSLSocketFactory;
42import javax.net.ssl.TrustManager;
43import javax.net.ssl.X509ExtendedTrustManager;
44import javax.net.ssl.X509TrustManager;
45import junit.framework.Assert;
46import libcore.java.security.StandardNames;
47import libcore.java.security.TestKeyStore;
48
49/**
50 * TestSSLContext is a convenience class for other tests that
51 * want a canned SSLContext and related state for testing so they
52 * don't have to duplicate the logic.
53 */
54public final class TestSSLContext extends Assert {
55
56    /*
57     * The RI and Android have very different default SSLSession cache behaviors.
58     * The RI keeps an unlimited number of SSLSesions around for 1 day.
59     * Android keeps 10 SSLSessions forever.
60     */
61    private static final boolean IS_RI = StandardNames.IS_RI;
62    public static final int EXPECTED_DEFAULT_CLIENT_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 10;
63    public static final int EXPECTED_DEFAULT_SERVER_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 100;
64    public static final int EXPECTED_DEFAULT_SSL_SESSION_CACHE_TIMEOUT =
65            (IS_RI) ? 24 * 3600 : 8 * 3600;
66
67    /**
68     * The Android SSLSocket and SSLServerSocket implementations are
69     * based on a version of OpenSSL which includes support for RFC
70     * 4507 session tickets. When using session tickets, the server
71     * does not need to keep a cache mapping session IDs to SSL
72     * sessions for reuse. Instead, the client presents the server
73     * with a session ticket it received from the server earlier,
74     * which is an SSL session encrypted by the server's secret
75     * key. Since in this case the server does not need to keep a
76     * cache, some tests may find different results depending on
77     * whether or not the session tickets are in use. These tests can
78     * use this function to determine if loopback SSL connections are
79     * expected to use session tickets and conditionalize their
80     * results appropriately.
81     */
82    public static boolean sslServerSocketSupportsSessionTickets () {
83        // Disabled session tickets for better compatability b/2682876
84        // return !IS_RI;
85        return false;
86    }
87
88    public final KeyStore clientKeyStore;
89    public final char[] clientStorePassword;
90    public final KeyStore serverKeyStore;
91    public final char[] serverStorePassword;
92    public final KeyManager[] clientKeyManagers;
93    public final KeyManager[] serverKeyManagers;
94    public final X509ExtendedTrustManager clientTrustManager;
95    public final X509ExtendedTrustManager serverTrustManager;
96    public final SSLContext clientContext;
97    public final SSLContext serverContext;
98    public final SSLServerSocket serverSocket;
99    public final InetAddress host;
100    public final int port;
101
102    /**
103     * Used for replacing the hostname in an InetSocketAddress object during
104     * serialization.
105     */
106    private static class HostnameRewritingObjectOutputStream extends ObjectOutputStream {
107        private final String hostname;
108
109        public HostnameRewritingObjectOutputStream(OutputStream out, String hostname)
110                throws IOException {
111            super(out);
112            this.hostname = hostname;
113        }
114
115        @Override
116        public PutField putFields() throws IOException {
117            return new PutFieldProxy(super.putFields(), hostname);
118        }
119
120        private static class PutFieldProxy extends ObjectOutputStream.PutField {
121            private final PutField delegate;
122            private final String hostname;
123
124            public PutFieldProxy(ObjectOutputStream.PutField delegate, String hostname) {
125                this.delegate = delegate;
126                this.hostname = hostname;
127            }
128
129            @Override
130            public void put(String name, boolean val) {
131                delegate.put(name, val);
132            }
133
134            @Override
135            public void put(String name, byte val) {
136                delegate.put(name, val);
137            }
138
139            @Override
140            public void put(String name, char val) {
141                delegate.put(name, val);
142            }
143
144            @Override
145            public void put(String name, short val) {
146                delegate.put(name, val);
147            }
148
149            @Override
150            public void put(String name, int val) {
151                delegate.put(name, val);
152            }
153
154            @Override
155            public void put(String name, long val) {
156                delegate.put(name, val);
157            }
158
159            @Override
160            public void put(String name, float val) {
161                delegate.put(name, val);
162            }
163
164            @Override
165            public void put(String name, double val) {
166                delegate.put(name, val);
167            }
168
169            @Override
170            public void put(String name, Object val) {
171                if ("hostname".equals(name)) {
172                    delegate.put(name, hostname);
173                } else {
174                    delegate.put(name, val);
175                }
176            }
177
178            @SuppressWarnings("deprecation")
179            @Override
180            public void write(ObjectOutput out) throws IOException {
181                delegate.write(out);
182            }
183        }
184    }
185
186    /**
187     * Creates an InetSocketAddress where the hostname points to an arbitrary
188     * hostname, but the address points to the loopback address. Useful for
189     * testing SNI where both "localhost" and IP addresses are not allowed.
190     */
191    public InetSocketAddress getLoopbackAsHostname(String hostname, int port) throws IOException, ClassNotFoundException {
192        InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), port);
193
194        ByteArrayOutputStream baos = new ByteArrayOutputStream();
195        HostnameRewritingObjectOutputStream oos = new HostnameRewritingObjectOutputStream(baos, hostname);
196        oos.writeObject(addr);
197        oos.close();
198
199        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
200        return (InetSocketAddress) ois.readObject();
201    }
202
203    private TestSSLContext(KeyStore clientKeyStore,
204                           char[] clientStorePassword,
205                           KeyStore serverKeyStore,
206                           char[] serverStorePassword,
207                           KeyManager[] clientKeyManagers,
208                           KeyManager[] serverKeyManagers,
209                           X509ExtendedTrustManager clientTrustManager,
210                           X509ExtendedTrustManager serverTrustManager,
211                           SSLContext clientContext,
212                           SSLContext serverContext,
213                           SSLServerSocket serverSocket,
214                           InetAddress host,
215                           int port) {
216        this.clientKeyStore = clientKeyStore;
217        this.clientStorePassword = clientStorePassword;
218        this.serverKeyStore = serverKeyStore;
219        this.serverStorePassword = serverStorePassword;
220        this.clientKeyManagers = clientKeyManagers;
221        this.serverKeyManagers = serverKeyManagers;
222        this.clientTrustManager = clientTrustManager;
223        this.serverTrustManager = serverTrustManager;
224        this.clientContext = clientContext;
225        this.serverContext = serverContext;
226        this.serverSocket = serverSocket;
227        this.host = host;
228        this.port = port;
229    }
230
231    public void close() {
232        try {
233            serverSocket.close();
234        } catch (Exception e) {
235            throw new RuntimeException(e);
236        }
237    }
238
239    /**
240     * Usual TestSSLContext creation method, creates underlying
241     * SSLContext with certificate and key as well as SSLServerSocket
242     * listening provided host and port.
243     */
244    public static TestSSLContext create() {
245        return create(TestKeyStore.getClient(),
246                      TestKeyStore.getServer());
247    }
248
249    /**
250     * TestSSLContext creation method that allows separate creation of server key store
251     */
252    public static TestSSLContext create(TestKeyStore client, TestKeyStore server) {
253        return createWithAdditionalKeyManagers(client, server, null, null);
254    }
255
256    /**
257     * TestSSLContext creation method that allows separate creation of server key store and
258     * the use of additional {@code KeyManager} instances
259     */
260    public static TestSSLContext createWithAdditionalKeyManagers(
261            TestKeyStore client, TestKeyStore server,
262            KeyManager[] additionalClientKeyManagers, KeyManager[] additionalServerKeyManagers) {
263        String protocol = "TLSv1.2";
264        KeyManager[] clientKeyManagers = concat(client.keyManagers, additionalClientKeyManagers);
265        KeyManager[] serverKeyManagers = concat(server.keyManagers, additionalServerKeyManagers);
266        SSLContext clientContext =
267                createSSLContext(protocol, clientKeyManagers, client.trustManagers);
268        SSLContext serverContext =
269                createSSLContext(protocol, serverKeyManagers, server.trustManagers);
270        return create(client.keyStore, client.storePassword,
271                      server.keyStore, server.storePassword,
272                      clientKeyManagers,
273                      serverKeyManagers,
274                      client.trustManagers[0],
275                      server.trustManagers[0],
276                      clientContext,
277                      serverContext);
278    }
279
280    /**
281     * TestSSLContext creation method that allows separate creation of client and server key store
282     */
283    public static TestSSLContext create(KeyStore clientKeyStore, char[] clientStorePassword,
284                                        KeyStore serverKeyStore, char[] serverStorePassword,
285                                        KeyManager[] clientKeyManagers,
286                                        KeyManager[] serverKeyManagers,
287                                        TrustManager clientTrustManagers,
288                                        TrustManager serverTrustManagers,
289                                        SSLContext clientContext,
290                                        SSLContext serverContext) {
291        try {
292            SSLServerSocket serverSocket = (SSLServerSocket)
293                serverContext.getServerSocketFactory().createServerSocket(0);
294            InetAddress host = InetAddress.getLocalHost();
295            int port = serverSocket.getLocalPort();
296
297            return new TestSSLContext(clientKeyStore, clientStorePassword,
298                                      serverKeyStore, serverStorePassword,
299                                      clientKeyManagers,
300                                      serverKeyManagers,
301                                      (X509ExtendedTrustManager) clientTrustManagers,
302                                      (X509ExtendedTrustManager) serverTrustManagers,
303                                      clientContext, serverContext,
304                                      serverSocket, host, port);
305        } catch (RuntimeException e) {
306            throw e;
307        } catch (Exception e) {
308            throw new RuntimeException(e);
309        }
310    }
311
312    /**
313     * Create a SSLContext with a KeyManager using the private key and
314     * certificate chain from the given KeyStore and a TrustManager
315     * using the certificates authorities from the same KeyStore.
316     */
317    public static final SSLContext createSSLContext(final String protocol,
318                                                    final KeyManager[] keyManagers,
319                                                    final TrustManager[] trustManagers)
320    {
321        try {
322            SSLContext context = SSLContext.getInstance(protocol);
323            context.init(keyManagers, trustManagers, new SecureRandom());
324            return context;
325        } catch (Exception e) {
326            throw new RuntimeException(e);
327        }
328    }
329
330    public static void assertCertificateInKeyStore(Principal principal,
331                                                   KeyStore keyStore) throws Exception {
332        String subjectName = principal.getName();
333        boolean found = false;
334        for (String alias: Collections.list(keyStore.aliases())) {
335            if (!keyStore.isCertificateEntry(alias)) {
336                continue;
337            }
338            X509Certificate keyStoreCertificate = (X509Certificate) keyStore.getCertificate(alias);
339            if (subjectName.equals(keyStoreCertificate.getSubjectDN().getName())) {
340                found = true;
341                break;
342            }
343        }
344        assertTrue(found);
345    }
346
347    public static void assertCertificateInKeyStore(Certificate certificate,
348                                                   KeyStore keyStore) throws Exception {
349        boolean found = false;
350        for (String alias: Collections.list(keyStore.aliases())) {
351            if (!keyStore.isCertificateEntry(alias)) {
352                continue;
353            }
354            Certificate keyStoreCertificate = keyStore.getCertificate(alias);
355            if (certificate.equals(keyStoreCertificate)) {
356                found = true;
357                break;
358            }
359        }
360        assertTrue(found);
361    }
362
363    public static void assertServerCertificateChain(X509TrustManager trustManager,
364                                                    Certificate[] serverChain)
365            throws CertificateException {
366        X509Certificate[] chain = (X509Certificate[]) serverChain;
367        trustManager.checkServerTrusted(chain, chain[0].getPublicKey().getAlgorithm());
368    }
369
370    public static void assertClientCertificateChain(X509TrustManager trustManager,
371                                                    Certificate[] clientChain)
372            throws CertificateException {
373        X509Certificate[] chain = (X509Certificate[]) clientChain;
374        trustManager.checkClientTrusted(chain, chain[0].getPublicKey().getAlgorithm());
375    }
376
377    /**
378     * Returns an SSLSocketFactory that calls setWantClientAuth and
379     * setNeedClientAuth as specified on all returned sockets.
380     */
381    public static SSLSocketFactory clientAuth(final SSLSocketFactory sf,
382                                              final boolean want,
383                                              final boolean need) {
384        return new SSLSocketFactory() {
385            private SSLSocket set(Socket socket) {
386                SSLSocket s = (SSLSocket) socket;
387                s.setWantClientAuth(want);
388                s.setNeedClientAuth(need);
389                return s;
390            }
391            public Socket createSocket(String host, int port)
392                    throws IOException, UnknownHostException {
393                return set(sf.createSocket(host, port));
394            }
395            public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
396                    throws IOException, UnknownHostException {
397                return set(sf.createSocket(host, port, localHost, localPort));
398            }
399            public Socket createSocket(InetAddress host, int port) throws IOException {
400                return set(sf.createSocket(host, port));
401            }
402            public Socket createSocket(InetAddress address, int port,
403                                       InetAddress localAddress, int localPort) throws IOException {
404                return set(sf.createSocket(address, port));
405            }
406
407            public String[] getDefaultCipherSuites() {
408                return sf.getDefaultCipherSuites();
409            }
410            public String[] getSupportedCipherSuites() {
411                return sf.getSupportedCipherSuites();
412            }
413
414            public Socket createSocket(Socket s, String host, int port, boolean autoClose)
415                    throws IOException {
416                return set(sf.createSocket(s, host, port, autoClose));
417            }
418        };
419    }
420
421    private static KeyManager[] concat(KeyManager[] a, KeyManager[] b) {
422        if ((a == null) || (a.length == 0)) {
423            return b;
424        }
425        if ((b == null) || (b.length == 0)) {
426            return a;
427        }
428        KeyManager[] result = new KeyManager[a.length + b.length];
429        System.arraycopy(a, 0, result, 0, a.length);
430        System.arraycopy(b, 0, result, a.length, b.length);
431        return result;
432    }
433}
434