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 }