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 }