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