1 /*
   2  * Copyright (c) 2011, 2013, 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 //
  25 // SunJSSE does not support dynamic system properties, no way to re-use
  26 // system properties in samevm/agentvm mode.
  27 //
  28 
  29 /*
  30  * @test
  31  * @bug 7105780
  32  * @summary Add SSLSocket client/SSLEngine server to templates directory.
  33  * @run main/othervm SSLSocketSSLEngineTemplate
  34  */
  35 
  36 /**
  37  * A SSLSocket/SSLEngine interop test case.  This is not the way to
  38  * code SSLEngine-based servers, but works for what we need to do here,
  39  * which is to make sure that SSLEngine/SSLSockets can talk to each other.
  40  * SSLEngines can use direct or indirect buffers, and different code
  41  * is used to get at the buffer contents internally, so we test that here.
  42  *
  43  * The test creates one SSLSocket (client) and one SSLEngine (server).
  44  * The SSLSocket talks to a raw ServerSocket, and the server code
  45  * does the translation between byte [] and ByteBuffers that the SSLEngine
  46  * can use.  The "transport" layer consists of a Socket Input/OutputStream
  47  * and two byte buffers for the SSLEngines:  think of them
  48  * as directly connected pipes.
  49  *
  50  * Again, this is a *very* simple example: real code will be much more
  51  * involved.  For example, different threading and I/O models could be
  52  * used, transport mechanisms could close unexpectedly, and so on.
  53  *
  54  * When this application runs, notice that several messages
  55  * (wrap/unwrap) pass before any application data is consumed or
  56  * produced.  (For more information, please see the SSL/TLS
  57  * specifications.)  There may several steps for a successful handshake,
  58  * so it's typical to see the following series of operations:
  59  *
  60  *      client          server          message
  61  *      ======          ======          =======
  62  *      write()         ...             ClientHello
  63  *      ...             unwrap()        ClientHello
  64  *      ...             wrap()          ServerHello/Certificate
  65  *      read()          ...             ServerHello/Certificate
  66  *      write()         ...             ClientKeyExchange
  67  *      write()         ...             ChangeCipherSpec
  68  *      write()         ...             Finished
  69  *      ...             unwrap()        ClientKeyExchange
  70  *      ...             unwrap()        ChangeCipherSpec
  71  *      ...             unwrap()        Finished
  72  *      ...             wrap()          ChangeCipherSpec
  73  *      ...             wrap()          Finished
  74  *      read()          ...             ChangeCipherSpec
  75  *      read()          ...             Finished
  76  */
  77 import javax.net.ssl.*;
  78 import javax.net.ssl.SSLEngineResult.*;
  79 import java.io.*;
  80 import java.net.*;
  81 import java.security.*;
  82 import java.nio.*;
  83 
  84 public class SSLSocketSSLEngineTemplate {
  85 
  86     /*
  87      * Enables logging of the SSL/TLS operations.
  88      */
  89     private static boolean logging = true;
  90 
  91     /*
  92      * Enables the JSSE system debugging system property:
  93      *
  94      *     -Djavax.net.debug=all
  95      *
  96      * This gives a lot of low-level information about operations underway,
  97      * including specific handshake messages, and might be best examined
  98      * after gaining some familiarity with this application.
  99      */
 100     private static boolean debug = false;
 101     private SSLContext sslc;
 102     private SSLEngine serverEngine;     // server-side SSLEngine
 103     private SSLSocket sslSocket;        // client-side socket
 104     private ServerSocket serverSocket;  // server-side Socket, generates the...
 105     private Socket socket;              // server-side socket that will read
 106 
 107     private final byte[] serverMsg =
 108         "Hi there Client, I'm a Server.".getBytes();
 109     private final byte[] clientMsg =
 110         "Hello Server, I'm a Client!  Pleased to meet you!".getBytes();
 111 
 112     private ByteBuffer serverOut;       // write side of serverEngine
 113     private ByteBuffer serverIn;        // read side of serverEngine
 114 
 115     private volatile Exception clientException;
 116     private volatile Exception serverException;
 117 
 118     /*
 119      * For data transport, this example uses local ByteBuffers.
 120      */
 121     private ByteBuffer cTOs;            // "reliable" transport client->server
 122     private ByteBuffer sTOc;            // "reliable" transport server->client
 123 
 124     /*
 125      * The following is to set up the keystores/trust material.
 126      */
 127     private static final String pathToStores = "../etc";
 128     private static final String keyStoreFile = "keystore";
 129     private static final String trustStoreFile = "truststore";
 130     private static final String passwd = "passphrase";
 131     private static String keyFilename =
 132             System.getProperty("test.src", ".") + "/" + pathToStores
 133             + "/" + keyStoreFile;
 134     private static String trustFilename =
 135             System.getProperty("test.src", ".") + "/" + pathToStores
 136             + "/" + trustStoreFile;
 137 
 138     /*
 139      * Main entry point for this test.
 140      */
 141     public static void main(String args[]) throws Exception {
 142         // reset security properties to make sure that the algorithms
 143         // and keys used in this test are not disabled.
 144         Security.setProperty("jdk.tls.disabledAlgorithms", "");
 145         Security.setProperty("jdk.certpath.disabledAlgorithms", "");
 146 
 147         if (debug) {
 148             System.setProperty("javax.net.debug", "all");
 149         }
 150 
 151         String [] protocols = new String [] {
 152             "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2" };
 153 
 154         for (String protocol : protocols) {
 155             log("Testing " + protocol);
 156             /*
 157              * Run the tests with direct and indirect buffers.
 158              */
 159             SSLSocketSSLEngineTemplate test =
 160                 new SSLSocketSSLEngineTemplate(protocol);
 161             log("-------------------------------------");
 162             log("Testing " + protocol + " for direct buffers ...");
 163             test.runTest(true);
 164 
 165             log("---------------------------------------");
 166             log("Testing " + protocol + " for indirect buffers ...");
 167             test.runTest(false);
 168         }
 169 
 170         System.out.println("Test Passed.");
 171     }
 172 
 173     /*
 174      * Create an initialized SSLContext to use for these tests.
 175      */
 176     public SSLSocketSSLEngineTemplate(String protocol) throws Exception {
 177 
 178         KeyStore ks = KeyStore.getInstance("JKS");
 179         KeyStore ts = KeyStore.getInstance("JKS");
 180 
 181         char[] passphrase = "passphrase".toCharArray();
 182 
 183         ks.load(new FileInputStream(keyFilename), passphrase);
 184         ts.load(new FileInputStream(trustFilename), passphrase);
 185 
 186         KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
 187         kmf.init(ks, passphrase);
 188 
 189         TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
 190         tmf.init(ts);
 191 
 192         SSLContext sslCtx = SSLContext.getInstance(protocol);
 193 
 194         sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 195 
 196         sslc = sslCtx;
 197     }
 198 
 199     /*
 200      * Run the test.
 201      *
 202      * Sit in a tight loop, with the server engine calling wrap/unwrap
 203      * regardless of whether data is available or not.  We do this until
 204      * we get the application data.  Then we shutdown and go to the next one.
 205      *
 206      * The main loop handles all of the I/O phases of the SSLEngine's
 207      * lifetime:
 208      *
 209      *     initial handshaking
 210      *     application data transfer
 211      *     engine closing
 212      *
 213      * One could easily separate these phases into separate
 214      * sections of code.
 215      */
 216     private void runTest(boolean direct) throws Exception {
 217         boolean serverClose = direct;
 218 
 219         serverSocket = new ServerSocket();
 220         serverSocket.setReuseAddress(false);
 221         serverSocket.bind(null);
 222         int port = serverSocket.getLocalPort();
 223         Thread thread = createClientThread(port, serverClose);
 224 
 225         socket = serverSocket.accept();
 226         socket.setSoTimeout(500);
 227         serverSocket.close();
 228 
 229         createSSLEngine();
 230         createBuffers(direct);
 231 
 232         try {
 233             boolean closed = false;
 234             // will try to read one more time in case client message
 235             // is fragmented to multiple pieces
 236             boolean retry = true;
 237 
 238             InputStream is = socket.getInputStream();
 239             OutputStream os = socket.getOutputStream();
 240 
 241             SSLEngineResult serverResult;   // results from last operation
 242 
 243             /*
 244              * Examining the SSLEngineResults could be much more involved,
 245              * and may alter the overall flow of the application.
 246              *
 247              * For example, if we received a BUFFER_OVERFLOW when trying
 248              * to write to the output pipe, we could reallocate a larger
 249              * pipe, but instead we wait for the peer to drain it.
 250              */
 251             byte[] inbound = new byte[8192];
 252             byte[] outbound = new byte[8192];
 253 
 254             while (!isEngineClosed(serverEngine)) {
 255                 int len = 0;
 256 
 257                 // Inbound data
 258                 log("================");
 259 
 260                 // Read from the Client side.
 261                 try {
 262                     len = is.read(inbound);
 263                     if (len == -1) {
 264                         throw new Exception("Unexpected EOF");
 265                     }
 266                     cTOs.put(inbound, 0, len);
 267                 } catch (SocketTimeoutException ste) {
 268                     // swallow.  Nothing yet, probably waiting on us.
 269                 }
 270 
 271                 cTOs.flip();
 272 
 273                 serverResult = serverEngine.unwrap(cTOs, serverIn);
 274                 log("server unwrap: ", serverResult);
 275                 runDelegatedTasks(serverResult, serverEngine);
 276                 cTOs.compact();
 277 
 278                 // Outbound data
 279                 log("----");
 280 
 281                 serverResult = serverEngine.wrap(serverOut, sTOc);
 282                 log("server wrap: ", serverResult);
 283                 runDelegatedTasks(serverResult, serverEngine);
 284 
 285                 sTOc.flip();
 286 
 287                 if ((len = sTOc.remaining()) != 0) {
 288                     sTOc.get(outbound, 0, len);
 289                     os.write(outbound, 0, len);
 290                     // Give the other side a chance to process
 291                 }
 292 
 293                 sTOc.compact();
 294 
 295                 if (!closed && (serverOut.remaining() == 0)) {
 296                     closed = true;
 297 
 298                     /*
 299                      * We'll alternate initiatating the shutdown.
 300                      * When the server initiates, it will take one more
 301                      * loop, but tests the orderly shutdown.
 302                      */
 303                     if (serverClose) {
 304                         serverEngine.closeOutbound();
 305                     }
 306                     serverIn.flip();
 307 
 308                     /*
 309                      * A sanity check to ensure we got what was sent.
 310                      */
 311                     if (serverIn.remaining() !=  clientMsg.length) {
 312                         if (retry && serverIn.remaining() < clientMsg.length) {
 313                             log("Need to read more from client");
 314                             retry = false;
 315                             continue;
 316                         } else {
 317                             throw new Exception("Client:  Data length error");
 318                         }
 319                     }
 320 
 321                     for (int i = 0; i < clientMsg.length; i++) {
 322                         if (clientMsg[i] != serverIn.get()) {
 323                             throw new Exception("Client:  Data content error");
 324                         }
 325                     }
 326                     serverIn.compact();
 327                 }
 328             }
 329             return;
 330         } catch (Exception e) {
 331             serverException = e;
 332         } finally {
 333             if (socket != null) {
 334                 socket.close();
 335             }
 336 
 337             // Wait for the client to join up with us.
 338             if (thread != null) {
 339                 thread.join();
 340             }
 341 
 342             if (sslSocket != null) {
 343                 sslSocket.close();
 344             }
 345 
 346             if (serverException != null) {
 347                 if (clientException != null) {
 348                     serverException.initCause(clientException);
 349                 }
 350                 throw serverException;
 351             }
 352             if (clientException != null) {
 353                 if (serverException != null) {
 354                     clientException.initCause(serverException);
 355                 }
 356                 throw clientException;
 357             }
 358         }
 359     }
 360 
 361     /*
 362      * Create a client thread which does simple SSLSocket operations.
 363      * We'll write and read one data packet.
 364      */
 365     private Thread createClientThread(final int port,
 366             final boolean serverClose) throws Exception {
 367 
 368         Thread t = new Thread("ClientThread") {
 369 
 370             @Override
 371             public void run() {
 372                 try {
 373                     Thread.sleep(1000);  // Give server time to finish setup.
 374 
 375                     sslSocket = (SSLSocket) sslc.getSocketFactory().
 376                             createSocket("localhost", port);
 377                     OutputStream os = sslSocket.getOutputStream();
 378                     InputStream is = sslSocket.getInputStream();
 379 
 380                     // write(byte[]) goes in one shot.
 381                     os.write(clientMsg);
 382 
 383                     byte[] inbound = new byte[2048];
 384                     int pos = 0;
 385 
 386                     int len;
 387 done:
 388                     while ((len = is.read(inbound, pos, 2048 - pos)) != -1) {
 389                         pos += len;
 390                         // Let the client do the closing.
 391                         if ((pos == serverMsg.length) && !serverClose) {
 392                             sslSocket.close();
 393                             break done;
 394                         }
 395                     }
 396 
 397                     if (pos != serverMsg.length) {
 398                         throw new Exception("Client:  Data length error");
 399                     }
 400 
 401                     for (int i = 0; i < serverMsg.length; i++) {
 402                         if (inbound[i] != serverMsg[i]) {
 403                             throw new Exception("Client:  Data content error");
 404                         }
 405                     }
 406                 } catch (Exception e) {
 407                     clientException = e;
 408                 }
 409             }
 410         };
 411         t.start();
 412         return t;
 413     }
 414 
 415     /*
 416      * Using the SSLContext created during object creation,
 417      * create/configure the SSLEngines we'll use for this test.
 418      */
 419     private void createSSLEngine() throws Exception {
 420         /*
 421          * Configure the serverEngine to act as a server in the SSL/TLS
 422          * handshake.
 423          */
 424         serverEngine = sslc.createSSLEngine();
 425         serverEngine.setUseClientMode(false);
 426         serverEngine.getNeedClientAuth();
 427     }
 428 
 429     /*
 430      * Create and size the buffers appropriately.
 431      */
 432     private void createBuffers(boolean direct) {
 433 
 434         SSLSession session = serverEngine.getSession();
 435         int appBufferMax = session.getApplicationBufferSize();
 436         int netBufferMax = session.getPacketBufferSize();
 437 
 438         /*
 439          * We'll make the input buffers a bit bigger than the max needed
 440          * size, so that unwrap()s following a successful data transfer
 441          * won't generate BUFFER_OVERFLOWS.
 442          *
 443          * We'll use a mix of direct and indirect ByteBuffers for
 444          * tutorial purposes only.  In reality, only use direct
 445          * ByteBuffers when they give a clear performance enhancement.
 446          */
 447         if (direct) {
 448             serverIn = ByteBuffer.allocateDirect(appBufferMax + 50);
 449             cTOs = ByteBuffer.allocateDirect(netBufferMax);
 450             sTOc = ByteBuffer.allocateDirect(netBufferMax);
 451         } else {
 452             serverIn = ByteBuffer.allocate(appBufferMax + 50);
 453             cTOs = ByteBuffer.allocate(netBufferMax);
 454             sTOc = ByteBuffer.allocate(netBufferMax);
 455         }
 456 
 457         serverOut = ByteBuffer.wrap(serverMsg);
 458     }
 459 
 460     /*
 461      * If the result indicates that we have outstanding tasks to do,
 462      * go ahead and run them in this thread.
 463      */
 464     private static void runDelegatedTasks(SSLEngineResult result,
 465             SSLEngine engine) throws Exception {
 466 
 467         if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
 468             Runnable runnable;
 469             while ((runnable = engine.getDelegatedTask()) != null) {
 470                 log("\trunning delegated task...");
 471                 runnable.run();
 472             }
 473             HandshakeStatus hsStatus = engine.getHandshakeStatus();
 474             if (hsStatus == HandshakeStatus.NEED_TASK) {
 475                 throw new Exception(
 476                         "handshake shouldn't need additional tasks");
 477             }
 478             log("\tnew HandshakeStatus: " + hsStatus);
 479         }
 480     }
 481 
 482     private static boolean isEngineClosed(SSLEngine engine) {
 483         return (engine.isOutboundDone() && engine.isInboundDone());
 484     }
 485 
 486     /*
 487      * Logging code
 488      */
 489     private static boolean resultOnce = true;
 490 
 491     private static void log(String str, SSLEngineResult result) {
 492         if (!logging) {
 493             return;
 494         }
 495         if (resultOnce) {
 496             resultOnce = false;
 497             System.out.println("The format of the SSLEngineResult is: \n"
 498                     + "\t\"getStatus() / getHandshakeStatus()\" +\n"
 499                     + "\t\"bytesConsumed() / bytesProduced()\"\n");
 500         }
 501         HandshakeStatus hsStatus = result.getHandshakeStatus();
 502         log(str
 503                 + result.getStatus() + "/" + hsStatus + ", "
 504                 + result.bytesConsumed() + "/" + result.bytesProduced()
 505                 + " bytes");
 506         if (hsStatus == HandshakeStatus.FINISHED) {
 507             log("\t...ready for application data");
 508         }
 509     }
 510 
 511     private static void log(String str) {
 512         if (logging) {
 513             System.out.println(str);
 514         }
 515     }
 516 }