1 /*
   2  * Copyright (c) 2004, 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 6207322
  32  * @summary SSLEngine is returning a premature FINISHED message when doing
  33  *     an abbreviated handshake.
  34  * @run main/othervm RehandshakeFinished
  35  * @author Brad Wetmore
  36  */
  37 
  38 /*
  39  * This test may need some updating if the messages change order.
  40  * Currently I'm expecting that there is a simple renegotiation, with
  41  * each message being contained in a single SSL packet.
  42  *
  43  *      ClientHello
  44  *                              Server Hello
  45  *                              CCS
  46  *                              FINISHED
  47  *      CCS
  48  *      FINISHED
  49  */
  50 
  51 /**
  52  * A SSLEngine usage example which simplifies the presentation
  53  * by removing the I/O and multi-threading concerns.
  54  *
  55  * The test creates two SSLEngines, simulating a client and server.
  56  * The "transport" layer consists two byte buffers:  think of them
  57  * as directly connected pipes.
  58  *
  59  * Note, this is a *very* simple example: real code will be much more
  60  * involved.  For example, different threading and I/O models could be
  61  * used, transport mechanisms could close unexpectedly, and so on.
  62  *
  63  * When this application runs, notice that several messages
  64  * (wrap/unwrap) pass before any application data is consumed or
  65  * produced.  (For more information, please see the SSL/TLS
  66  * specifications.)  There may several steps for a successful handshake,
  67  * so it's typical to see the following series of operations:
  68  *
  69  *      client          server          message
  70  *      ======          ======          =======
  71  *      wrap()          ...             ClientHello
  72  *      ...             unwrap()        ClientHello
  73  *      ...             wrap()          ServerHello/Certificate
  74  *      unwrap()        ...             ServerHello/Certificate
  75  *      wrap()          ...             ClientKeyExchange
  76  *      wrap()          ...             ChangeCipherSpec
  77  *      wrap()          ...             Finished
  78  *      ...             unwrap()        ClientKeyExchange
  79  *      ...             unwrap()        ChangeCipherSpec
  80  *      ...             unwrap()        Finished
  81  *      ...             wrap()          ChangeCipherSpec
  82  *      ...             wrap()          Finished
  83  *      unwrap()        ...             ChangeCipherSpec
  84  *      unwrap()        ...             Finished
  85  */
  86 
  87 import javax.net.ssl.*;
  88 import javax.net.ssl.SSLEngineResult.*;
  89 import java.io.*;
  90 import java.security.*;
  91 import java.nio.*;
  92 
  93 public class RehandshakeFinished {
  94 
  95     /*
  96      * Enables logging of the SSLEngine operations.
  97      */
  98     private static boolean logging = true;
  99 
 100     /*
 101      * Enables the JSSE system debugging system property:
 102      *
 103      *     -Djavax.net.debug=all
 104      *
 105      * This gives a lot of low-level information about operations underway,
 106      * including specific handshake messages, and might be best examined
 107      * after gaining some familiarity with this application.
 108      */
 109     private static boolean debug = false;
 110 
 111     static private SSLContext sslc;
 112 
 113     private SSLEngine clientEngine;     // client Engine
 114     private ByteBuffer clientOut;       // write side of clientEngine
 115     private ByteBuffer clientIn;        // read side of clientEngine
 116 
 117     private SSLEngine serverEngine;     // server Engine
 118     private ByteBuffer serverOut;       // write side of serverEngine
 119     private ByteBuffer serverIn;        // read side of serverEngine
 120 
 121     /*
 122      * For data transport, this example uses local ByteBuffers.  This
 123      * isn't really useful, but the purpose of this example is to show
 124      * SSLEngine concepts, not how to do network transport.
 125      */
 126     private ByteBuffer cTOs;            // "reliable" transport client->server
 127     private ByteBuffer sTOc;            // "reliable" transport server->client
 128 
 129     /*
 130      * The following is to set up the keystores.
 131      */
 132     private static String pathToStores = "../../../../javax/net/ssl/etc";
 133     private static String keyStoreFile = "keystore";
 134     private static String trustStoreFile = "truststore";
 135     private static String passwd = "passphrase";
 136 
 137     private static String keyFilename =
 138             System.getProperty("test.src", "./") + "/" + pathToStores +
 139                 "/" + keyStoreFile;
 140     private static String trustFilename =
 141             System.getProperty("test.src", "./") + "/" + pathToStores +
 142                 "/" + trustStoreFile;
 143 
 144     private static Exception loadException = null;
 145 
 146     static {
 147         try {
 148             KeyStore ks = KeyStore.getInstance("JKS");
 149             KeyStore ts = KeyStore.getInstance("JKS");
 150 
 151             char[] passphrase = "passphrase".toCharArray();
 152 
 153             ks.load(new FileInputStream(keyFilename), passphrase);
 154             ts.load(new FileInputStream(trustFilename), passphrase);
 155 
 156             KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
 157             kmf.init(ks, passphrase);
 158 
 159             TrustManagerFactory tmf =
 160                 TrustManagerFactory.getInstance("SunX509");
 161             tmf.init(ts);
 162 
 163             SSLContext sslCtx = SSLContext.getInstance("TLSv1.2");
 164             sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 165             sslc = sslCtx;
 166         } catch (Exception e) {
 167             loadException = e;
 168         }
 169     }
 170 
 171     /*
 172      * Main entry point for this test.
 173      */
 174     public static void main(String args[]) throws Exception {
 175         if (debug) {
 176             System.setProperty("javax.net.debug", "all");
 177         }
 178 
 179         if (loadException != null) {
 180             throw loadException;
 181         }
 182 
 183         // Prime the session cache with a good session
 184         // Second connection should be a simple session resumption.
 185         if ((new RehandshakeFinished().runTest()) !=
 186                 new RehandshakeFinished().runRehandshake()) {
 187             throw new Exception("Sessions not equivalent");
 188         }
 189 
 190         System.out.println("Test Passed.");
 191     }
 192 
 193     private void checkResult(SSLEngine engine, SSLEngineResult result,
 194             HandshakeStatus rqdHsStatus,
 195             boolean consumed, boolean produced) throws Exception {
 196 
 197         HandshakeStatus hsStatus = result.getHandshakeStatus();
 198 
 199         if (hsStatus == HandshakeStatus.NEED_TASK) {
 200             Runnable runnable;
 201             while ((runnable = engine.getDelegatedTask()) != null) {
 202                 runnable.run();
 203             }
 204             hsStatus = engine.getHandshakeStatus();
 205         }
 206 
 207         if (hsStatus != rqdHsStatus) {
 208             throw new Exception("Required " + rqdHsStatus +
 209                 ", got " + hsStatus);
 210         }
 211 
 212         int bc = result.bytesConsumed();
 213         int bp = result.bytesProduced();
 214 
 215         if (consumed) {
 216             if (bc <= 0) {
 217                 throw new Exception("Should have consumed bytes");
 218             }
 219         } else {
 220             if (bc > 0) {
 221                 throw new Exception("Should not have consumed bytes");
 222             }
 223         }
 224 
 225         if (produced) {
 226             if (bp <= 0) {
 227                 throw new Exception("Should have produced bytes");
 228             }
 229         } else {
 230             if (bp > 0) {
 231                 throw new Exception("Should not have produced bytes");
 232             }
 233         }
 234     }
 235 
 236     private SSLSession runRehandshake() throws Exception {
 237 
 238         log("\n\n==============================================");
 239         log("Staring actual test.");
 240 
 241         createSSLEngines();
 242         createBuffers();
 243         SSLEngineResult result;
 244 
 245         log("Client's ClientHello");
 246         checkResult(clientEngine,
 247             clientEngine.wrap(clientOut, cTOs), HandshakeStatus.NEED_UNWRAP,
 248             false, true);
 249         cTOs.flip();
 250         checkResult(serverEngine,
 251             serverEngine.unwrap(cTOs, serverIn), HandshakeStatus.NEED_WRAP,
 252             true, false);
 253         cTOs.compact();
 254 
 255         log("Server's ServerHello/ServerHelloDone");
 256         checkResult(serverEngine,
 257             serverEngine.wrap(serverOut, sTOc), HandshakeStatus.NEED_WRAP,
 258             false, true);
 259         sTOc.flip();
 260         checkResult(clientEngine,
 261             clientEngine.unwrap(sTOc, clientIn), HandshakeStatus.NEED_UNWRAP,
 262             true, false);
 263         sTOc.compact();
 264 
 265         log("Server's CCS");
 266         checkResult(serverEngine,
 267             serverEngine.wrap(serverOut, sTOc), HandshakeStatus.NEED_WRAP,
 268             false, true);
 269         sTOc.flip();
 270         checkResult(clientEngine,
 271             clientEngine.unwrap(sTOc, clientIn), HandshakeStatus.NEED_UNWRAP,
 272             true, false);
 273         sTOc.compact();
 274 
 275         log("Server's FINISHED");
 276         checkResult(serverEngine,
 277             serverEngine.wrap(serverOut, sTOc), HandshakeStatus.NEED_UNWRAP,
 278             false, true);
 279         sTOc.flip();
 280         checkResult(clientEngine,
 281             clientEngine.unwrap(sTOc, clientIn), HandshakeStatus.NEED_WRAP,
 282             true, false);
 283         sTOc.compact();
 284 
 285         log("Client's CCS");
 286         checkResult(clientEngine,
 287             clientEngine.wrap(clientOut, cTOs), HandshakeStatus.NEED_WRAP,
 288             false, true);
 289         cTOs.flip();
 290         checkResult(serverEngine,
 291             serverEngine.unwrap(cTOs, serverIn), HandshakeStatus.NEED_UNWRAP,
 292             true, false);
 293         cTOs.compact();
 294 
 295         log("Client's FINISHED should trigger FINISHED messages all around.");
 296         checkResult(clientEngine,
 297             clientEngine.wrap(clientOut, cTOs), HandshakeStatus.FINISHED,
 298             false, true);
 299         cTOs.flip();
 300         checkResult(serverEngine,
 301             serverEngine.unwrap(cTOs, serverIn), HandshakeStatus.FINISHED,
 302             true, false);
 303         cTOs.compact();
 304 
 305         return clientEngine.getSession();
 306     }
 307 
 308     /*
 309      * Run the test.
 310      *
 311      * Sit in a tight loop, both engines calling wrap/unwrap regardless
 312      * of whether data is available or not.  We do this until both engines
 313      * report back they are closed.
 314      *
 315      * The main loop handles all of the I/O phases of the SSLEngine's
 316      * lifetime:
 317      *
 318      *     initial handshaking
 319      *     application data transfer
 320      *     engine closing
 321      *
 322      * One could easily separate these phases into separate
 323      * sections of code.
 324      */
 325     private SSLSession runTest() throws Exception {
 326         boolean dataDone = false;
 327 
 328         createSSLEngines();
 329         createBuffers();
 330 
 331         SSLEngineResult clientResult;   // results from client's last operation
 332         SSLEngineResult serverResult;   // results from server's last operation
 333 
 334         /*
 335          * Examining the SSLEngineResults could be much more involved,
 336          * and may alter the overall flow of the application.
 337          *
 338          * For example, if we received a BUFFER_OVERFLOW when trying
 339          * to write to the output pipe, we could reallocate a larger
 340          * pipe, but instead we wait for the peer to drain it.
 341          */
 342         while (!isEngineClosed(clientEngine) ||
 343                 !isEngineClosed(serverEngine)) {
 344 
 345             log("================");
 346 
 347             clientResult = clientEngine.wrap(clientOut, cTOs);
 348             log("client wrap: ", clientResult);
 349             runDelegatedTasks(clientResult, clientEngine);
 350 
 351             serverResult = serverEngine.wrap(serverOut, sTOc);
 352             log("server wrap: ", serverResult);
 353             runDelegatedTasks(serverResult, serverEngine);
 354 
 355             cTOs.flip();
 356             sTOc.flip();
 357 
 358             log("----");
 359 
 360             clientResult = clientEngine.unwrap(sTOc, clientIn);
 361             log("client unwrap: ", clientResult);
 362             runDelegatedTasks(clientResult, clientEngine);
 363 
 364             serverResult = serverEngine.unwrap(cTOs, serverIn);
 365             log("server unwrap: ", serverResult);
 366             runDelegatedTasks(serverResult, serverEngine);
 367 
 368             cTOs.compact();
 369             sTOc.compact();
 370 
 371             /*
 372              * After we've transfered all application data between the client
 373              * and server, we close the clientEngine's outbound stream.
 374              * This generates a close_notify handshake message, which the
 375              * server engine receives and responds by closing itself.
 376              */
 377             if (!dataDone && (clientOut.limit() == serverIn.position()) &&
 378                     (serverOut.limit() == clientIn.position())) {
 379 
 380                 /*
 381                  * A sanity check to ensure we got what was sent.
 382                  */
 383                 checkTransfer(serverOut, clientIn);
 384                 checkTransfer(clientOut, serverIn);
 385 
 386                 log("\tClosing clientEngine's *OUTBOUND*...");
 387                 clientEngine.closeOutbound();
 388                 dataDone = true;
 389             }
 390         }
 391 
 392         return clientEngine.getSession();
 393     }
 394 
 395     /*
 396      * Using the SSLContext created during object creation,
 397      * create/configure the SSLEngines we'll use for this test.
 398      */
 399     private void createSSLEngines() throws Exception {
 400         /*
 401          * Configure the serverEngine to act as a server in the SSL/TLS
 402          * handshake.  Also, require SSL client authentication.
 403          */
 404         serverEngine = sslc.createSSLEngine();
 405         serverEngine.setUseClientMode(false);
 406         serverEngine.setNeedClientAuth(true);
 407 
 408         /*
 409          * Similar to above, but using client mode instead.
 410          */
 411         clientEngine = sslc.createSSLEngine("client", 80);
 412         clientEngine.setUseClientMode(true);
 413     }
 414 
 415     /*
 416      * Create and size the buffers appropriately.
 417      */
 418     private void createBuffers() {
 419 
 420         /*
 421          * We'll assume the buffer sizes are the same
 422          * between client and server.
 423          */
 424         SSLSession session = clientEngine.getSession();
 425         int appBufferMax = session.getApplicationBufferSize();
 426         int netBufferMax = session.getPacketBufferSize();
 427 
 428         /*
 429          * We'll make the input buffers a bit bigger than the max needed
 430          * size, so that unwrap()s following a successful data transfer
 431          * won't generate BUFFER_OVERFLOWS.
 432          *
 433          * We'll use a mix of direct and indirect ByteBuffers for
 434          * tutorial purposes only.  In reality, only use direct
 435          * ByteBuffers when they give a clear performance enhancement.
 436          */
 437         clientIn = ByteBuffer.allocate(appBufferMax + 50);
 438         serverIn = ByteBuffer.allocate(appBufferMax + 50);
 439 
 440         cTOs = ByteBuffer.allocateDirect(netBufferMax);
 441         sTOc = ByteBuffer.allocateDirect(netBufferMax);
 442 
 443         clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
 444         serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
 445     }
 446 
 447     /*
 448      * If the result indicates that we have outstanding tasks to do,
 449      * go ahead and run them in this thread.
 450      */
 451     private static void runDelegatedTasks(SSLEngineResult result,
 452             SSLEngine engine) throws Exception {
 453 
 454         if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
 455             Runnable runnable;
 456             while ((runnable = engine.getDelegatedTask()) != null) {
 457                 log("\trunning delegated task...");
 458                 runnable.run();
 459             }
 460             HandshakeStatus hsStatus = engine.getHandshakeStatus();
 461             if (hsStatus == HandshakeStatus.NEED_TASK) {
 462                 throw new Exception(
 463                     "handshake shouldn't need additional tasks");
 464             }
 465             log("\tnew HandshakeStatus: " + hsStatus);
 466         }
 467     }
 468 
 469     private static boolean isEngineClosed(SSLEngine engine) {
 470         return (engine.isOutboundDone() && engine.isInboundDone());
 471     }
 472 
 473     /*
 474      * Simple check to make sure everything came across as expected.
 475      */
 476     private static void checkTransfer(ByteBuffer a, ByteBuffer b)
 477             throws Exception {
 478         a.flip();
 479         b.flip();
 480 
 481         if (!a.equals(b)) {
 482             throw new Exception("Data didn't transfer cleanly");
 483         } else {
 484             log("\tData transferred cleanly");
 485         }
 486 
 487         a.position(a.limit());
 488         b.position(b.limit());
 489         a.limit(a.capacity());
 490         b.limit(b.capacity());
 491     }
 492 
 493     /*
 494      * Logging code
 495      */
 496     private static boolean resultOnce = true;
 497 
 498     private static void log(String str, SSLEngineResult result) {
 499         if (!logging) {
 500             return;
 501         }
 502         if (resultOnce) {
 503             resultOnce = false;
 504             System.out.println("The format of the SSLEngineResult is: \n" +
 505                 "\t\"getStatus() / getHandshakeStatus()\" +\n" +
 506                 "\t\"bytesConsumed() / bytesProduced()\"\n");
 507         }
 508         HandshakeStatus hsStatus = result.getHandshakeStatus();
 509         log(str +
 510             result.getStatus() + "/" + hsStatus + ", " +
 511             result.bytesConsumed() + "/" + result.bytesProduced() +
 512             " bytes");
 513         if (hsStatus == HandshakeStatus.FINISHED) {
 514             log("\t...ready for application data");
 515         }
 516     }
 517 
 518     private static void log(String str) {
 519         if (logging) {
 520             System.out.println(str);
 521         }
 522     }
 523 }