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