SSLSocketTemplate.java revision 16073:1ed36f639166
1/*
2 * Copyright (c) 2001, 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
24import java.io.FileInputStream;
25import java.io.IOException;
26import java.io.InputStream;
27import java.io.OutputStream;
28import java.net.InetSocketAddress;
29import java.net.SocketTimeoutException;
30import java.security.KeyStore;
31import java.security.Security;
32import java.util.concurrent.CountDownLatch;
33import java.util.concurrent.TimeUnit;
34import javax.net.ssl.SSLContext;
35import javax.net.ssl.SSLServerSocket;
36import javax.net.ssl.SSLServerSocketFactory;
37import javax.net.ssl.SSLSocket;
38import javax.net.ssl.SSLSocketFactory;
39
40/**
41 * This class defines a framework for JSSE tests.
42 *
43 * Please run in othervm mode.  SunJSSE does not support dynamic system
44 * properties, no way to re-use system properties in samevm/agentvm mode.
45 */
46
47/*
48 * @test
49 * @bug 1234567
50 * @summary Use this class for JSSE tests
51 * @run main/othervm SSLSocketTemplate
52 * @author Brad Wetmore
53 */
54
55public class SSLSocketTemplate {
56
57    public static final String TEST_SRC = System.getProperty("test.src", ".");
58
59    /*
60     * Where do we find the keystores?
61     */
62    public static final String PATH_TO_STORES = "../etc";
63    public static final String KEY_STORE_FILE = "keystore";
64    public static final String TRUST_STORE_FILE = "truststore";
65    public static final String PASSWORD = "passphrase";
66
67    public static final int FREE_PORT = 0;
68
69    // in seconds
70    public static final long CLIENT_SIGNAL_TIMEOUT = 30L;
71    public static final long SERVER_SIGNAL_TIMEOUT = 90L;
72
73    // in millis
74    public static final int CLIENT_TIMEOUT = 15000;
75    public static final int SERVER_TIMEOUT = 30000;
76
77    /*
78     * Should we run the client or server in a separate thread?
79     * Both sides can throw exceptions, but do you have a preference
80     * as to which side should be the main thread.
81     */
82    private boolean separateServerThread = false;
83
84    /*
85     * What's the server port?  Use any free port by default
86     */
87    private volatile int serverPort;
88
89    private volatile Exception serverException;
90    private volatile Exception clientException;
91
92    private Thread clientThread;
93    private Thread serverThread;
94
95    private Peer serverPeer;
96    private Peer clientPeer;
97
98    private Application serverApplication;
99    private Application clientApplication;
100
101    private SSLContext context;
102
103    /*
104     * Is the server ready to serve?
105     */
106    private final CountDownLatch serverReadyCondition = new CountDownLatch(1);
107
108    /*
109     * Is the client ready to handshake?
110     */
111    private final CountDownLatch clientReadyCondition = new CountDownLatch(1);
112
113    /*
114     * Is the server done?
115     */
116    private final CountDownLatch serverDoneCondition = new CountDownLatch(1);
117
118    /*
119     * Is the client done?
120     */
121    private final CountDownLatch clientDoneCondition = new CountDownLatch(1);
122
123    /*
124     * Public API.
125     */
126
127    public static interface Peer {
128        void run(SSLSocketTemplate test) throws Exception;
129    }
130
131    public static interface Application {
132        void run(SSLSocket socket, SSLSocketTemplate test) throws Exception;
133    }
134
135    public static void debug() {
136        debug("ssl");
137    }
138
139    public static void debug(String mode) {
140        System.setProperty("javax.net.debug", mode);
141    }
142
143    public static void setup(String keyFilename, String trustFilename,
144            String password) {
145
146        System.setProperty("javax.net.ssl.keyStore", keyFilename);
147        System.setProperty("javax.net.ssl.keyStorePassword", password);
148        System.setProperty("javax.net.ssl.trustStore", trustFilename);
149        System.setProperty("javax.net.ssl.trustStorePassword", password);
150    }
151
152    public static void setup() throws Exception {
153        String keyFilename = TEST_SRC + "/" + PATH_TO_STORES + "/"
154                + KEY_STORE_FILE;
155        String trustFilename = TEST_SRC + "/" + PATH_TO_STORES + "/"
156                + TRUST_STORE_FILE;
157
158        setup(keyFilename, trustFilename, PASSWORD);
159    }
160
161    public static void print(String message, Throwable... errors) {
162        synchronized (System.out) {
163            System.out.println(message);
164            for (Throwable e : errors) {
165                e.printStackTrace(System.out);
166            }
167        }
168    }
169
170    public static KeyStore loadJksKeyStore(String filename, String password)
171            throws Exception {
172
173        return loadKeyStore(filename, password, "JKS");
174    }
175
176    public static KeyStore loadKeyStore(String filename, String password,
177            String type) throws Exception {
178
179        KeyStore keystore = KeyStore.getInstance(type);
180        FileInputStream fis = new FileInputStream(filename);
181        try {
182            keystore.load(fis, password.toCharArray());
183        } finally {
184            fis.close();
185        }
186        return keystore;
187    }
188
189    // Try to accept a connection in 30 seconds.
190    public static SSLSocket accept(SSLServerSocket sslServerSocket)
191            throws IOException {
192
193        return accept(sslServerSocket, SERVER_TIMEOUT);
194    }
195
196    public static SSLSocket accept(SSLServerSocket sslServerSocket, int timeout)
197            throws IOException {
198
199        try {
200            sslServerSocket.setSoTimeout(timeout);
201            return (SSLSocket) sslServerSocket.accept();
202        } catch (SocketTimeoutException ste) {
203            print("Warning: ", ste);
204            return null;
205        }
206    }
207
208    public SSLSocketTemplate setSeparateServerThread(
209            boolean separateServerThread) {
210
211        this.separateServerThread = separateServerThread;
212        return this;
213    }
214
215    public SSLSocketTemplate setServerPort(int serverPort) {
216        this.serverPort = serverPort;
217        return this;
218    }
219
220    public int getServerPort() {
221        return serverPort;
222    }
223
224    public SSLSocketTemplate setSSLContext(SSLContext context) {
225        this.context = context;
226        return this;
227    }
228
229    public SSLContext getSSLContext() {
230        return context;
231    }
232
233    public SSLServerSocketFactory getSSLServerSocketFactory() {
234        if (context != null) {
235            return context.getServerSocketFactory();
236        }
237
238        return (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
239    }
240
241    public SSLSocketFactory getSSLSocketFactory() {
242        if (context != null) {
243            return context.getSocketFactory();
244        }
245
246        return (SSLSocketFactory) SSLSocketFactory.getDefault();
247    }
248
249    public void signalServerReady() {
250        serverReadyCondition.countDown();
251    }
252
253    public void signalServerDone() {
254        serverDoneCondition.countDown();
255    }
256
257    public boolean waitForClientSignal(long timeout, TimeUnit unit)
258            throws InterruptedException {
259
260        return clientReadyCondition.await(timeout, unit);
261    }
262
263    public boolean waitForClientSignal() throws InterruptedException {
264        return waitForClientSignal(CLIENT_SIGNAL_TIMEOUT, TimeUnit.SECONDS);
265    }
266
267    public boolean waitForClientDone(long timeout, TimeUnit unit)
268            throws InterruptedException {
269
270        return clientDoneCondition.await(timeout, unit);
271    }
272
273    public boolean waitForClientDone() throws InterruptedException {
274        return waitForClientDone(CLIENT_SIGNAL_TIMEOUT, TimeUnit.SECONDS);
275    }
276
277    public void signalClientReady() {
278        clientReadyCondition.countDown();
279    }
280
281    public void signalClientDone() {
282        clientDoneCondition.countDown();
283    }
284
285    public boolean waitForServerSignal(long timeout, TimeUnit unit)
286            throws InterruptedException {
287
288        return serverReadyCondition.await(timeout, unit);
289    }
290
291    public boolean waitForServerSignal() throws InterruptedException {
292        return waitForServerSignal(SERVER_SIGNAL_TIMEOUT, TimeUnit.SECONDS);
293    }
294
295    public boolean waitForServerDone(long timeout, TimeUnit unit)
296            throws InterruptedException {
297
298        return serverDoneCondition.await(timeout, unit);
299    }
300
301    public boolean waitForServerDone() throws InterruptedException {
302        return waitForServerDone(SERVER_SIGNAL_TIMEOUT, TimeUnit.SECONDS);
303    }
304
305    public SSLSocketTemplate setServerPeer(Peer serverPeer) {
306        this.serverPeer = serverPeer;
307        return this;
308    }
309
310    public Peer getServerPeer() {
311        return serverPeer;
312    }
313
314    public SSLSocketTemplate setServerApplication(
315            Application serverApplication) {
316
317        this.serverApplication = serverApplication;
318        return this;
319    }
320
321    public Application getServerApplication() {
322        return serverApplication;
323    }
324
325    public SSLSocketTemplate setClientPeer(Peer clientPeer) {
326        this.clientPeer = clientPeer;
327        return this;
328    }
329
330    public Peer getClientPeer() {
331        return clientPeer;
332    }
333
334    public SSLSocketTemplate setClientApplication(
335            Application clientApplication) {
336
337        this.clientApplication = clientApplication;
338        return this;
339    }
340
341    public Application getClientApplication() {
342        return clientApplication;
343    }
344
345    public void runTest() throws Exception {
346        if (separateServerThread) {
347            startServer(true, this);
348            startClient(false, this);
349            serverThread.join();
350        } else {
351            startClient(true, this);
352            startServer(false, this);
353            clientThread.join();
354        }
355
356        if (clientException != null || serverException != null) {
357            throw new RuntimeException("Test failed");
358        }
359    }
360
361    public SSLSocketTemplate() {
362        serverPeer = new Peer() {
363
364            @Override
365            public void run(SSLSocketTemplate test) throws Exception {
366                doServerSide(test);
367            }
368        };
369
370        clientPeer = new Peer() {
371
372            @Override
373            public void run(SSLSocketTemplate test) throws Exception {
374                doClientSide(test);
375            }
376        };
377
378        serverApplication = new Application() {
379
380            @Override
381            public void run(SSLSocket socket, SSLSocketTemplate test)
382                    throws Exception {
383
384                runServerApplication(socket);
385            }
386
387        };
388
389        clientApplication = new Application() {
390
391            @Override
392            public void run(SSLSocket socket, SSLSocketTemplate test)
393                    throws Exception {
394
395                runClientApplication(socket);
396            }
397        };
398    }
399
400    public static void main(String args[]) throws Exception {
401        // reset the security property to make sure that the algorithms
402        // and keys used in this test are not disabled.
403        Security.setProperty("jdk.tls.disabledAlgorithms", "");
404
405        // MD5 is used in this test case, don't disable MD5 algorithm.
406        Security.setProperty(
407                "jdk.certpath.disabledAlgorithms", "MD2, RSA keySize < 1024");
408
409        setup();
410
411        new SSLSocketTemplate().runTest();
412    }
413
414    /*
415     * Private part.
416     */
417
418    /*
419     * Define the server side of the test.
420     */
421    private static void doServerSide(SSLSocketTemplate test) throws Exception {
422        SSLServerSocket sslServerSocket;
423
424        // kick start the server side service
425        SSLServerSocketFactory sslssf = test.getSSLServerSocketFactory();
426        sslServerSocket = (SSLServerSocket)sslssf.createServerSocket(FREE_PORT);
427
428        test.setServerPort(sslServerSocket.getLocalPort());
429        print("Server is listening on port " + test.getServerPort());
430
431        // Signal the client, the server is ready to accept connection.
432        test.signalServerReady();
433
434        // Try to accept a connection in 30 seconds.
435        SSLSocket sslSocket = accept(sslServerSocket);
436        if (sslSocket == null) {
437            // Ignore the test case if no connection within 30 seconds.
438            print("No incoming client connection in 30 seconds. "
439                    + "Ignore in server side.");
440            return;
441        }
442        print("Server accepted connection");
443
444        // handle the connection
445        try {
446            // Is it the expected client connection?
447            //
448            // Naughty test cases or third party routines may try to
449            // connection to this server port unintentionally.  In
450            // order to mitigate the impact of unexpected client
451            // connections and avoid intermittent failure, it should
452            // be checked that the accepted connection is really linked
453            // to the expected client.
454            boolean clientIsReady = test.waitForClientSignal();
455
456            if (clientIsReady) {
457                // Run the application in server side.
458                print("Run server application");
459                test.getServerApplication().run(sslSocket, test);
460            } else {    // Otherwise, ignore
461                // We don't actually care about plain socket connections
462                // for TLS communication testing generally.  Just ignore
463                // the test if the accepted connection is not linked to
464                // the expected client or the client connection timeout
465                // in 30 seconds.
466                print("The client is not the expected one or timeout. "
467                        + "Ignore in server side.");
468            }
469        } finally {
470            sslSocket.close();
471            sslServerSocket.close();
472        }
473
474        test.signalServerDone();
475    }
476
477    /*
478     * Define the server side application of the test for the specified socket.
479     */
480    private static void runServerApplication(SSLSocket socket)
481            throws Exception {
482
483        // here comes the test logic
484        InputStream sslIS = socket.getInputStream();
485        OutputStream sslOS = socket.getOutputStream();
486
487        sslIS.read();
488        sslOS.write(85);
489        sslOS.flush();
490    }
491
492    /*
493     * Define the client side of the test.
494     */
495    private static void doClientSide(SSLSocketTemplate test) throws Exception {
496
497        // Wait for server to get started.
498        //
499        // The server side takes care of the issue if the server cannot
500        // get started in 90 seconds.  The client side would just ignore
501        // the test case if the serer is not ready.
502        boolean serverIsReady = test.waitForServerSignal();
503        if (!serverIsReady) {
504            print("The server is not ready yet in 90 seconds. "
505                    + "Ignore in client side.");
506            return;
507        }
508
509        SSLSocketFactory sslsf = test.getSSLSocketFactory();
510        SSLSocket sslSocket = (SSLSocket)sslsf.createSocket();
511        try {
512            try {
513                sslSocket.connect(
514                        new InetSocketAddress("localhost",
515                                test.getServerPort()), CLIENT_TIMEOUT);
516                print("Client connected to server");
517            } catch (IOException ioe) {
518                // The server side may be impacted by naughty test cases or
519                // third party routines, and cannot accept connections.
520                //
521                // Just ignore the test if the connection cannot be
522                // established.
523                print("Cannot make a connection in 15 seconds. "
524                        + "Ignore in client side.", ioe);
525                return;
526            }
527
528            // OK, here the client and server get connected.
529
530            // Signal the server, the client is ready to communicate.
531            test.signalClientReady();
532
533            // There is still a chance in theory that the server thread may
534            // wait client-ready timeout and then quit.  The chance should
535            // be really rare so we don't consider it until it becomes a
536            // real problem.
537
538            // Run the application in client side.
539            print("Run client application");
540            test.getClientApplication().run(sslSocket, test);
541        } finally {
542            sslSocket.close();
543        }
544
545        test.signalClientDone();
546    }
547
548    /*
549     * Define the client side application of the test for the specified socket.
550     */
551    private static void runClientApplication(SSLSocket socket)
552            throws Exception {
553
554        InputStream sslIS = socket.getInputStream();
555        OutputStream sslOS = socket.getOutputStream();
556
557        sslOS.write(280);
558        sslOS.flush();
559        sslIS.read();
560    }
561
562    private void startServer(boolean newThread, SSLSocketTemplate test)
563            throws Exception {
564
565        if (newThread) {
566            serverThread = new Thread() {
567
568                @Override
569                public void run() {
570                    try {
571                        serverPeer.run(test);
572                    } catch (Exception e) {
573                        /*
574                         * Our server thread just died.
575                         *
576                         * Release the client, if not active already...
577                         */
578                        print("Server died ...", e);
579                        serverException = e;
580                    }
581                }
582            };
583            serverThread.start();
584        } else {
585            try {
586                serverPeer.run(test);
587            } catch (Exception e) {
588                print("Server failed ...", e);
589                serverException = e;
590            }
591        }
592    }
593
594    private void startClient(boolean newThread, SSLSocketTemplate test)
595            throws Exception {
596
597        if (newThread) {
598            clientThread = new Thread() {
599
600                @Override
601                public void run() {
602                    try {
603                        clientPeer.run(test);
604                    } catch (Exception e) {
605                        /*
606                         * Our client thread just died.
607                         */
608                        print("Client died ...", e);
609                        clientException = e;
610                    }
611                }
612            };
613            clientThread.start();
614        } else {
615            try {
616                clientPeer.run(test);
617            } catch (Exception e) {
618                print("Client failed ...", e);
619                clientException = e;
620            }
621        }
622    }
623}
624