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