1 /*
   2  * Copyright (c) 2003, 2015, 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 // SunJSSE does not support dynamic system properties, no way to re-use
  25 // system properties in samevm/agentvm mode.
  26 
  27 /*
  28  * @test
  29  * @bug 1234567
  30  * @summary SSLEngine has not yet caused Solaris kernel to panic
  31  * @run main/othervm SSLEngineTemplate
  32  */
  33 
  34 /**
  35  * A SSLEngine usage example which simplifies the presentation
  36  * by removing the I/O and multi-threading concerns.
  37  *
  38  * The test creates two SSLEngines, simulating a client and server.
  39  * The "transport" layer consists two byte buffers:  think of them
  40  * as directly connected pipes.
  41  *
  42  * Note, this is a *very* simple example: real code will be much more
  43  * involved.  For example, different threading and I/O models could be
  44  * used, transport mechanisms could close unexpectedly, and so on.
  45  *
  46  * When this application runs, notice that several messages
  47  * (wrap/unwrap) pass before any application data is consumed or
  48  * produced.  (For more information, please see the SSL/TLS
  49  * specifications.)  There may several steps for a successful handshake,
  50  * so it's typical to see the following series of operations:
  51  *
  52  *      client          server          message
  53  *      ======          ======          =======
  54  *      wrap()          ...             ClientHello
  55  *      ...             unwrap()        ClientHello
  56  *      ...             wrap()          ServerHello/Certificate
  57  *      unwrap()        ...             ServerHello/Certificate
  58  *      wrap()          ...             ClientKeyExchange
  59  *      wrap()          ...             ChangeCipherSpec
  60  *      wrap()          ...             Finished
  61  *      ...             unwrap()        ClientKeyExchange
  62  *      ...             unwrap()        ChangeCipherSpec
  63  *      ...             unwrap()        Finished
  64  *      ...             wrap()          ChangeCipherSpec
  65  *      ...             wrap()          Finished
  66  *      unwrap()        ...             ChangeCipherSpec
  67  *      unwrap()        ...             Finished
  68  */
  69 
  70 import javax.net.ssl.*;
  71 import javax.net.ssl.SSLEngineResult.*;
  72 import java.io.*;
  73 import java.security.*;
  74 import java.nio.*;
  75 
  76 public class SSLEngineTemplate {
  77 
  78     /*
  79      * Enables logging of the SSLEngine operations.
  80      */
  81     private static final boolean logging = true;
  82 
  83     /*
  84      * Enables the JSSE system debugging system property:
  85      *
  86      *     -Djavax.net.debug=all
  87      *
  88      * This gives a lot of low-level information about operations underway,
  89      * including specific handshake messages, and might be best examined
  90      * after gaining some familiarity with this application.
  91      */
  92     private static final boolean debug = false;
  93 
  94     private final SSLContext sslc;
  95 
  96     private SSLEngine clientEngine;     // client Engine
  97     private ByteBuffer clientOut;       // write side of clientEngine
  98     private ByteBuffer clientIn;        // read side of clientEngine
  99 
 100     private SSLEngine serverEngine;     // server Engine
 101     private ByteBuffer serverOut;       // write side of serverEngine
 102     private ByteBuffer serverIn;        // read side of serverEngine
 103 
 104     /*
 105      * For data transport, this example uses local ByteBuffers.  This
 106      * isn't really useful, but the purpose of this example is to show
 107      * SSLEngine concepts, not how to do network transport.
 108      */
 109     private ByteBuffer cTOs;            // "reliable" transport client->server
 110     private ByteBuffer sTOc;            // "reliable" transport server->client
 111 
 112     /*
 113      * The following is to set up the keystores.
 114      */
 115     private static final String pathToStores = "../etc";
 116     private static final String keyStoreFile = "keystore";
 117     private static final String trustStoreFile = "truststore";
 118     private static final String passwd = "passphrase";
 119 
 120     private static final String keyFilename =
 121             System.getProperty("test.src", ".") + "/" + pathToStores +
 122                 "/" + keyStoreFile;
 123     private static final String trustFilename =
 124             System.getProperty("test.src", ".") + "/" + pathToStores +
 125                 "/" + trustStoreFile;
 126 
 127     /*
 128      * Main entry point for this test.
 129      */
 130     public static void main(String args[]) throws Exception {
 131         if (debug) {
 132             System.setProperty("javax.net.debug", "all");
 133         }
 134 
 135         SSLEngineTemplate test = new SSLEngineTemplate();
 136         test.runTest();
 137 
 138         System.out.println("Test Passed.");
 139     }
 140 
 141     /*
 142      * Create an initialized SSLContext to use for these tests.
 143      */
 144     public SSLEngineTemplate() throws Exception {
 145 
 146         KeyStore ks = KeyStore.getInstance("JKS");
 147         KeyStore ts = KeyStore.getInstance("JKS");
 148 
 149         char[] passphrase = "passphrase".toCharArray();
 150 
 151         ks.load(new FileInputStream(keyFilename), passphrase);
 152         ts.load(new FileInputStream(trustFilename), passphrase);
 153 
 154         KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
 155         kmf.init(ks, passphrase);
 156 
 157         TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
 158         tmf.init(ts);
 159 
 160         SSLContext sslCtx = SSLContext.getInstance("TLS");
 161 
 162         sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 163 
 164         sslc = sslCtx;
 165     }
 166 
 167     /*
 168      * Run the test.
 169      *
 170      * Sit in a tight loop, both engines calling wrap/unwrap regardless
 171      * of whether data is available or not.  We do this until both engines
 172      * report back they are closed.
 173      *
 174      * The main loop handles all of the I/O phases of the SSLEngine's
 175      * lifetime:
 176      *
 177      *     initial handshaking
 178      *     application data transfer
 179      *     engine closing
 180      *
 181      * One could easily separate these phases into separate
 182      * sections of code.
 183      */
 184     private void runTest() throws Exception {
 185         boolean dataDone = false;
 186 
 187         createSSLEngines();
 188         createBuffers();
 189 
 190         SSLEngineResult clientResult;   // results from client's last operation
 191         SSLEngineResult serverResult;   // results from server's last operation
 192 
 193         /*
 194          * Examining the SSLEngineResults could be much more involved,
 195          * and may alter the overall flow of the application.
 196          *
 197          * For example, if we received a BUFFER_OVERFLOW when trying
 198          * to write to the output pipe, we could reallocate a larger
 199          * pipe, but instead we wait for the peer to drain it.
 200          */
 201         while (!isEngineClosed(clientEngine) ||
 202                 !isEngineClosed(serverEngine)) {
 203 
 204             log("================");
 205 
 206             clientResult = clientEngine.wrap(clientOut, cTOs);
 207             log("client wrap: ", clientResult);
 208             runDelegatedTasks(clientResult, clientEngine);
 209 
 210             serverResult = serverEngine.wrap(serverOut, sTOc);
 211             log("server wrap: ", serverResult);
 212             runDelegatedTasks(serverResult, serverEngine);
 213 
 214             cTOs.flip();
 215             sTOc.flip();
 216 
 217             log("----");
 218 
 219             clientResult = clientEngine.unwrap(sTOc, clientIn);
 220             log("client unwrap: ", clientResult);
 221             runDelegatedTasks(clientResult, clientEngine);
 222 
 223             serverResult = serverEngine.unwrap(cTOs, serverIn);
 224             log("server unwrap: ", serverResult);
 225             runDelegatedTasks(serverResult, serverEngine);
 226 
 227             cTOs.compact();
 228             sTOc.compact();
 229 
 230             /*
 231              * After we've transfered all application data between the client
 232              * and server, we close the clientEngine's outbound stream.
 233              * This generates a close_notify handshake message, which the
 234              * server engine receives and responds by closing itself.
 235              */
 236             if (!dataDone && (clientOut.limit() == serverIn.position()) &&
 237                     (serverOut.limit() == clientIn.position())) {
 238 
 239                 /*
 240                  * A sanity check to ensure we got what was sent.
 241                  */
 242                 checkTransfer(serverOut, clientIn);
 243                 checkTransfer(clientOut, serverIn);
 244 
 245                 log("\tClosing clientEngine's *OUTBOUND*...");
 246                 clientEngine.closeOutbound();
 247                 dataDone = true;
 248             }
 249         }
 250     }
 251 
 252     /*
 253      * Using the SSLContext created during object creation,
 254      * create/configure the SSLEngines we'll use for this test.
 255      */
 256     private void createSSLEngines() throws Exception {
 257         /*
 258          * Configure the serverEngine to act as a server in the SSL/TLS
 259          * handshake.  Also, require SSL client authentication.
 260          */
 261         serverEngine = sslc.createSSLEngine();
 262         serverEngine.setUseClientMode(false);
 263         serverEngine.setNeedClientAuth(true);
 264 
 265         /*
 266          * Similar to above, but using client mode instead.
 267          */
 268         clientEngine = sslc.createSSLEngine("client", 80);
 269         clientEngine.setUseClientMode(true);
 270     }
 271 
 272     /*
 273      * Create and size the buffers appropriately.
 274      */
 275     private void createBuffers() {
 276 
 277         /*
 278          * We'll assume the buffer sizes are the same
 279          * between client and server.
 280          */
 281         SSLSession session = clientEngine.getSession();
 282         int appBufferMax = session.getApplicationBufferSize();
 283         int netBufferMax = session.getPacketBufferSize();
 284 
 285         /*
 286          * We'll make the input buffers a bit bigger than the max needed
 287          * size, so that unwrap()s following a successful data transfer
 288          * won't generate BUFFER_OVERFLOWS.
 289          *
 290          * We'll use a mix of direct and indirect ByteBuffers for
 291          * tutorial purposes only.  In reality, only use direct
 292          * ByteBuffers when they give a clear performance enhancement.
 293          */
 294         clientIn = ByteBuffer.allocate(appBufferMax + 50);
 295         serverIn = ByteBuffer.allocate(appBufferMax + 50);
 296 
 297         cTOs = ByteBuffer.allocateDirect(netBufferMax);
 298         sTOc = ByteBuffer.allocateDirect(netBufferMax);
 299 
 300         clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
 301         serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
 302     }
 303 
 304     /*
 305      * If the result indicates that we have outstanding tasks to do,
 306      * go ahead and run them in this thread.
 307      */
 308     private static void runDelegatedTasks(SSLEngineResult result,
 309             SSLEngine engine) throws Exception {
 310 
 311         if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
 312             Runnable runnable;
 313             while ((runnable = engine.getDelegatedTask()) != null) {
 314                 log("\trunning delegated task...");
 315                 runnable.run();
 316             }
 317             HandshakeStatus hsStatus = engine.getHandshakeStatus();
 318             if (hsStatus == HandshakeStatus.NEED_TASK) {
 319                 throw new Exception(
 320                     "handshake shouldn't need additional tasks");
 321             }
 322             log("\tnew HandshakeStatus: " + hsStatus);
 323         }
 324     }
 325 
 326     private static boolean isEngineClosed(SSLEngine engine) {
 327         return (engine.isOutboundDone() && engine.isInboundDone());
 328     }
 329 
 330     /*
 331      * Simple check to make sure everything came across as expected.
 332      */
 333     private static void checkTransfer(ByteBuffer a, ByteBuffer b)
 334             throws Exception {
 335         a.flip();
 336         b.flip();
 337 
 338         if (!a.equals(b)) {
 339             throw new Exception("Data didn't transfer cleanly");
 340         } else {
 341             log("\tData transferred cleanly");
 342         }
 343 
 344         a.position(a.limit());
 345         b.position(b.limit());
 346         a.limit(a.capacity());
 347         b.limit(b.capacity());
 348     }
 349 
 350     /*
 351      * Logging code
 352      */
 353     private static boolean resultOnce = true;
 354 
 355     private static void log(String str, SSLEngineResult result) {
 356         if (!logging) {
 357             return;
 358         }
 359         if (resultOnce) {
 360             resultOnce = false;
 361             System.out.println("The format of the SSLEngineResult is: \n" +
 362                 "\t\"getStatus() / getHandshakeStatus()\" +\n" +
 363                 "\t\"bytesConsumed() / bytesProduced()\"\n");
 364         }
 365         HandshakeStatus hsStatus = result.getHandshakeStatus();
 366         log(str +
 367             result.getStatus() + "/" + hsStatus + ", " +
 368             result.bytesConsumed() + "/" + result.bytesProduced() +
 369             " bytes");
 370         if (hsStatus == HandshakeStatus.FINISHED) {
 371             log("\t...ready for application data");
 372         }
 373     }
 374 
 375     private static void log(String str) {
 376         if (logging) {
 377             System.out.println(str);
 378         }
 379     }
 380 }