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 }