1/*
2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24// SunJSSE does not support dynamic system properties, no way to re-use
25// system properties in samevm/agentvm mode.
26
27/*
28 * @test
29 * @bug 8148108
30 * @summary Disable Diffie-Hellman keys less than 1024 bits
31 * @run main/othervm -Djdk.tls.ephemeralDHKeySize=legacy LegacyDHEKeyExchange
32 */
33
34import java.io.*;
35import javax.net.ssl.*;
36
37public class LegacyDHEKeyExchange {
38
39    /*
40     * =============================================================
41     * Set the various variables needed for the tests, then
42     * specify what tests to run on each side.
43     */
44
45    /*
46     * Should we run the client or server in a separate thread?
47     * Both sides can throw exceptions, but do you have a preference
48     * as to which side should be the main thread.
49     */
50    static boolean separateServerThread = false;
51
52    /*
53     * Where do we find the keystores?
54     */
55    static String pathToStores = "../../../../javax/net/ssl/etc";
56    static String keyStoreFile = "keystore";
57    static String trustStoreFile = "truststore";
58    static String passwd = "passphrase";
59
60    /*
61     * Is the server ready to serve?
62     */
63    volatile static boolean serverReady = false;
64
65    /*
66     * Turn on SSL debugging?
67     */
68    static boolean debug = false;
69
70    /*
71     * If the client or server is doing some kind of object creation
72     * that the other side depends on, and that thread prematurely
73     * exits, you may experience a hang.  The test harness will
74     * terminate all hung threads after its timeout has expired,
75     * currently 3 minutes by default, but you might try to be
76     * smart about it....
77     */
78
79    /*
80     * Define the server side of the test.
81     *
82     * If the server prematurely exits, serverReady will be set to true
83     * to avoid infinite hangs.
84     */
85    void doServerSide() throws Exception {
86        SSLServerSocketFactory sslssf =
87            (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
88        SSLServerSocket sslServerSocket =
89            (SSLServerSocket) sslssf.createServerSocket(serverPort);
90
91        serverPort = sslServerSocket.getLocalPort();
92
93        /*
94         * Signal Client, we're ready for his connect.
95         */
96        serverReady = true;
97
98        try (SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept()) {
99            InputStream sslIS = sslSocket.getInputStream();
100            OutputStream sslOS = sslSocket.getOutputStream();
101
102            sslIS.read();
103            sslOS.write(85);
104            sslOS.flush();
105
106            throw new Exception(
107                "Leagcy DH keys (< 1024) should be restricted");
108        } catch (SSLHandshakeException she) {
109            // ignore, client should terminate the connection
110        } finally {
111            sslServerSocket.close();
112        }
113    }
114
115    /*
116     * Define the client side of the test.
117     *
118     * If the server prematurely exits, serverReady will be set to true
119     * to avoid infinite hangs.
120     */
121    void doClientSide() throws Exception {
122
123        /*
124         * Wait for server to get started.
125         */
126        while (!serverReady) {
127            Thread.sleep(50);
128        }
129
130        SSLSocketFactory sslsf =
131            (SSLSocketFactory) SSLSocketFactory.getDefault();
132        SSLSocket sslSocket = (SSLSocket)
133            sslsf.createSocket("localhost", serverPort);
134
135        String[] suites = new String [] {"TLS_DHE_RSA_WITH_AES_128_CBC_SHA"};
136        sslSocket.setEnabledCipherSuites(suites);
137
138        try {
139            InputStream sslIS = sslSocket.getInputStream();
140            OutputStream sslOS = sslSocket.getOutputStream();
141
142            sslOS.write(280);
143            sslOS.flush();
144            sslIS.read();
145
146            throw new Exception("Leagcy DH keys (< 1024) should be restricted");
147        } catch (SSLHandshakeException she) {
148            // ignore, should be caused by algorithm constraints
149        } finally {
150            sslSocket.close();
151        }
152    }
153
154    /*
155     * =============================================================
156     * The remainder is just support stuff
157     */
158
159    // use any free port by default
160    volatile int serverPort = 0;
161
162    volatile Exception serverException = null;
163    volatile Exception clientException = null;
164
165    public static void main(String[] args) throws Exception {
166        String keyFilename =
167            System.getProperty("test.src", ".") + "/" + pathToStores +
168                "/" + keyStoreFile;
169        String trustFilename =
170            System.getProperty("test.src", ".") + "/" + pathToStores +
171                "/" + trustStoreFile;
172
173        System.setProperty("javax.net.ssl.keyStore", keyFilename);
174        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
175        System.setProperty("javax.net.ssl.trustStore", trustFilename);
176        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
177
178        if (debug) {
179            System.setProperty("javax.net.debug", "all");
180        }
181
182        /*
183         * Start the tests.
184         */
185        new LegacyDHEKeyExchange();
186    }
187
188    Thread clientThread = null;
189    Thread serverThread = null;
190
191    /*
192     * Primary constructor, used to drive remainder of the test.
193     *
194     * Fork off the other side, then do your work.
195     */
196    LegacyDHEKeyExchange() throws Exception {
197        Exception startException = null;
198        try {
199            if (separateServerThread) {
200                startServer(true);
201                startClient(false);
202            } else {
203                startClient(true);
204                startServer(false);
205            }
206        } catch (Exception e) {
207            startException = e;
208        }
209
210        /*
211         * Wait for other side to close down.
212         */
213        if (separateServerThread) {
214            if (serverThread != null) {
215                serverThread.join();
216            }
217        } else {
218            if (clientThread != null) {
219                clientThread.join();
220            }
221        }
222
223        /*
224         * When we get here, the test is pretty much over.
225         * Which side threw the error?
226         */
227        Exception local;
228        Exception remote;
229
230        if (separateServerThread) {
231            remote = serverException;
232            local = clientException;
233        } else {
234            remote = clientException;
235            local = serverException;
236        }
237
238        Exception exception = null;
239
240        /*
241         * Check various exception conditions.
242         */
243        if ((local != null) && (remote != null)) {
244            // If both failed, return the curthread's exception.
245            local.initCause(remote);
246            exception = local;
247        } else if (local != null) {
248            exception = local;
249        } else if (remote != null) {
250            exception = remote;
251        } else if (startException != null) {
252            exception = startException;
253        }
254
255        /*
256         * If there was an exception *AND* a startException,
257         * output it.
258         */
259        if (exception != null) {
260            if (exception != startException && startException != null) {
261                exception.addSuppressed(startException);
262            }
263            throw exception;
264        }
265
266        // Fall-through: no exception to throw!
267    }
268
269    void startServer(boolean newThread) throws Exception {
270        if (newThread) {
271            serverThread = new Thread() {
272                @Override
273                public void run() {
274                    try {
275                        doServerSide();
276                    } catch (Exception e) {
277                        /*
278                         * Our server thread just died.
279                         *
280                         * Release the client, if not active already...
281                         */
282                        System.err.println("Server died...");
283                        serverReady = true;
284                        serverException = e;
285                    }
286                }
287            };
288            serverThread.start();
289        } else {
290            try {
291                doServerSide();
292            } catch (Exception e) {
293                serverException = e;
294            } finally {
295                serverReady = true;
296            }
297        }
298    }
299
300    void startClient(boolean newThread) throws Exception {
301        if (newThread) {
302            clientThread = new Thread() {
303                @Override
304                public void run() {
305                    try {
306                        doClientSide();
307                    } catch (Exception e) {
308                        /*
309                         * Our client thread just died.
310                         */
311                        System.err.println("Client died...");
312                        clientException = e;
313                    }
314                }
315            };
316            clientThread.start();
317        } else {
318            try {
319                doClientSide();
320            } catch (Exception e) {
321                clientException = e;
322            }
323        }
324    }
325}
326