TestSSLContext.java revision 9a106a63508697a6f5f02c20b7cc6b7c6152695f
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 javax.net.ssl;
18
19import java.net.InetAddress;
20import java.net.InetSocketAddress;
21import java.security.KeyStore;
22import java.security.Principal;
23import java.security.SecureRandom;
24import java.security.Security;
25import java.security.StandardNames;
26import java.security.cert.Certificate;
27import java.security.cert.X509Certificate;
28import java.util.Collections;
29import junit.framework.Assert;
30import org.bouncycastle.jce.provider.BouncyCastleProvider;
31
32/**
33 * TestSSLContext is a convenience class for other tests that
34 * want a canned SSLContext and related state for testing so they
35 * don't have to duplicate the logic.
36 */
37public final class TestSSLContext extends Assert {
38
39    /*
40     * The RI and Android have very different default SSLSession cache behaviors.
41     * The RI keeps an unlimited number of SSLSesions around for 1 day.
42     * Android keeps 10 SSLSessions forever.
43     */
44    private static final boolean IS_RI = StandardNames.IS_RI;
45    public static final int EXPECTED_DEFAULT_CLIENT_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 10;
46    public static final int EXPECTED_DEFAULT_SERVER_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 100;
47    public static final int EXPECTED_DEFAULT_SSL_SESSION_CACHE_TIMEOUT = (IS_RI) ? 86400 : 0;
48    static {
49        if (IS_RI) {
50            Security.addProvider(new BouncyCastleProvider());
51        }
52    }
53
54    /**
55     * The Android SSLSocket and SSLServerSocket implementations are
56     * based on a version of OpenSSL which includes support for RFC
57     * 4507 session tickets. When using session tickets, the server
58     * does not need to keep a cache mapping session IDs to SSL
59     * sessions for reuse. Instead, the client presents the server
60     * with a session ticket it received from the server earlier,
61     * which is an SSL session encrypted by the server's secret
62     * key. Since in this case the server does not need to keep a
63     * cache, some tests may find different results depending on
64     * whether or not the session tickets are in use. These tests can
65     * use this function to determine if loopback SSL connections are
66     * expected to use session tickets and conditionalize their
67     * results appropriately.
68     */
69    public static boolean sslServerSocketSupportsSessionTickets () {
70        // Disabled session tickets for better compatability b/2682876
71        // return !IS_RI;
72        return false;
73    }
74
75    public final KeyStore keyStore;
76    public final char[] keyStorePassword;
77    public final SSLContext sslContext;
78    public final SSLServerSocket serverSocket;
79    public final InetAddress host;
80    public final int port;
81
82    private TestSSLContext(KeyStore keyStore,
83                           char[] keyStorePassword,
84                           SSLContext sslContext,
85                           SSLServerSocket serverSocket,
86                           InetAddress host,
87                           int port) {
88        this.keyStore = keyStore;
89        this.keyStorePassword = keyStorePassword;
90        this.sslContext = sslContext;
91        this.serverSocket = serverSocket;
92        this.host = host;
93        this.port = port;
94    }
95
96    /**
97     * Usual TestSSLContext creation method, creates underlying
98     * SSLContext with certificate and key as well as SSLServerSocket
99     * listening provided host and port.
100     */
101    public static TestSSLContext create() {
102        TestKeyStore testKeyStore = TestKeyStore.get();
103        return create(testKeyStore.keyStore, testKeyStore.keyStorePassword);
104    }
105
106    /**
107     * TestSSLContext creation method that allows separate creation of key store
108     */
109    public static TestSSLContext create(KeyStore keyStore, char[] keyStorePassword) {
110        try {
111            SSLContext sslContext = createSSLContext(keyStore, keyStorePassword);
112
113            SSLServerSocket serverSocket = (SSLServerSocket)
114                sslContext.getServerSocketFactory().createServerSocket(0);
115            InetSocketAddress sa = (InetSocketAddress) serverSocket.getLocalSocketAddress();
116            InetAddress host = sa.getAddress();
117            int port = sa.getPort();
118
119            return new TestSSLContext(keyStore, keyStorePassword,
120                                      sslContext, serverSocket, host, port);
121        } catch (RuntimeException e) {
122            throw e;
123        } catch (Exception e) {
124            throw new RuntimeException(e);
125        }
126    }
127
128    /**
129     * Create a client version of the server TestSSLContext. The
130     * client will trust the server's certificate, but not contain any
131     * keys of its own.
132     */
133    public static TestSSLContext createClient(TestSSLContext server) {
134        try {
135            KeyStore keyStore = KeyStore.getInstance("BKS");
136            keyStore.load(null, null);
137            for (String alias: Collections.list(server.keyStore.aliases())) {
138                if (!server.keyStore.isCertificateEntry(alias)) {
139                    continue;
140                }
141                Certificate cert = server.keyStore.getCertificate(alias);
142                keyStore.setCertificateEntry(alias, cert);
143            }
144
145            char[] keyStorePassword = server.keyStorePassword;
146
147            String tmfa = TrustManagerFactory.getDefaultAlgorithm();
148            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfa);
149            tmf.init(keyStore);
150
151            SSLContext sslContext = SSLContext.getInstance("TLS");
152            sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
153
154            return new TestSSLContext(keyStore, keyStorePassword,
155                                      sslContext, null, null, -1);
156        } catch (RuntimeException e) {
157            throw e;
158        } catch (Exception e) {
159            throw new RuntimeException(e);
160        }
161    }
162
163    /**
164     * Create a SSLContext with a KeyManager using the private key and
165     * certificate chain from the given KeyStore and a TrustManager
166     * using the certificates authorities from the same KeyStore.
167     */
168    public static final SSLContext createSSLContext(final KeyStore keyStore,
169                                                    final char[] keyStorePassword)
170        throws Exception {
171        String kmfa = KeyManagerFactory.getDefaultAlgorithm();
172        KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfa);
173        kmf.init(keyStore, keyStorePassword);
174
175        String tmfa = TrustManagerFactory.getDefaultAlgorithm();
176        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfa);
177        tmf.init(keyStore);
178
179        SSLContext context = SSLContext.getInstance("TLS");
180        context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
181        return context;
182    }
183
184    public static void assertCertificateInKeyStore(Principal principal,
185                                                   KeyStore keyStore) throws Exception {
186        String subjectName = principal.getName();
187        boolean found = false;
188        for (String alias: Collections.list(keyStore.aliases())) {
189            if (!keyStore.isCertificateEntry(alias)) {
190                continue;
191            }
192            X509Certificate keyStoreCertificate = (X509Certificate) keyStore.getCertificate(alias);
193            if (subjectName.equals(keyStoreCertificate.getSubjectDN().getName())) {
194                found = true;
195                break;
196            }
197        }
198        assertTrue(found);
199    }
200
201    public static void assertCertificateInKeyStore(Certificate certificate,
202                                                   KeyStore keyStore) throws Exception {
203        boolean found = false;
204        for (String alias: Collections.list(keyStore.aliases())) {
205            if (!keyStore.isCertificateEntry(alias)) {
206                continue;
207            }
208            Certificate keyStoreCertificate = keyStore.getCertificate(alias);
209            if (certificate.equals(keyStoreCertificate)) {
210                found = true;
211                break;
212            }
213        }
214        assertTrue(found);
215    }
216}
217