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