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 }