1 /* 2 * Copyright (c) 2011, 2013, 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 7105780 32 * @summary Add SSLSocket client/SSLEngine server to templates directory. 33 * @run main/othervm SSLSocketSSLEngineTemplate 34 */ 35 36 /** 37 * A SSLSocket/SSLEngine interop test case. This is not the way to 38 * code SSLEngine-based servers, but works for what we need to do here, 39 * which is to make sure that SSLEngine/SSLSockets can talk to each other. 40 * SSLEngines can use direct or indirect buffers, and different code 41 * is used to get at the buffer contents internally, so we test that here. 42 * 43 * The test creates one SSLSocket (client) and one SSLEngine (server). 44 * The SSLSocket talks to a raw ServerSocket, and the server code 45 * does the translation between byte [] and ByteBuffers that the SSLEngine 46 * can use. The "transport" layer consists of a Socket Input/OutputStream 47 * and two byte buffers for the SSLEngines: think of them 48 * as directly connected pipes. 49 * 50 * Again, this is a *very* simple example: real code will be much more 51 * involved. For example, different threading and I/O models could be 52 * used, transport mechanisms could close unexpectedly, and so on. 53 * 54 * When this application runs, notice that several messages 55 * (wrap/unwrap) pass before any application data is consumed or 56 * produced. (For more information, please see the SSL/TLS 57 * specifications.) There may several steps for a successful handshake, 58 * so it's typical to see the following series of operations: 59 * 60 * client server message 61 * ====== ====== ======= 62 * write() ... ClientHello 63 * ... unwrap() ClientHello 64 * ... wrap() ServerHello/Certificate 65 * read() ... ServerHello/Certificate 66 * write() ... ClientKeyExchange 67 * write() ... ChangeCipherSpec 68 * write() ... Finished 69 * ... unwrap() ClientKeyExchange 70 * ... unwrap() ChangeCipherSpec 71 * ... unwrap() Finished 72 * ... wrap() ChangeCipherSpec 73 * ... wrap() Finished 74 * read() ... ChangeCipherSpec 75 * read() ... Finished 76 */ 77 import javax.net.ssl.*; 78 import javax.net.ssl.SSLEngineResult.*; 79 import java.io.*; 80 import java.net.*; 81 import java.security.*; 82 import java.nio.*; 83 84 public class SSLSocketSSLEngineTemplate { 85 86 /* 87 * Enables logging of the SSL/TLS operations. 88 */ 89 private static boolean logging = true; 90 91 /* 92 * Enables the JSSE system debugging system property: 93 * 94 * -Djavax.net.debug=all 95 * 96 * This gives a lot of low-level information about operations underway, 97 * including specific handshake messages, and might be best examined 98 * after gaining some familiarity with this application. 99 */ 100 private static boolean debug = false; 101 private SSLContext sslc; 102 private SSLEngine serverEngine; // server-side SSLEngine 103 private SSLSocket sslSocket; // client-side socket 104 private ServerSocket serverSocket; // server-side Socket, generates the... 105 private Socket socket; // server-side socket that will read 106 107 private final byte[] serverMsg = 108 "Hi there Client, I'm a Server.".getBytes(); 109 private final byte[] clientMsg = 110 "Hello Server, I'm a Client! Pleased to meet you!".getBytes(); 111 112 private ByteBuffer serverOut; // write side of serverEngine 113 private ByteBuffer serverIn; // read side of serverEngine 114 115 private volatile Exception clientException; 116 private volatile Exception serverException; 117 118 /* 119 * For data transport, this example uses local ByteBuffers. 120 */ 121 private ByteBuffer cTOs; // "reliable" transport client->server 122 private ByteBuffer sTOc; // "reliable" transport server->client 123 124 /* 125 * The following is to set up the keystores/trust material. 126 */ 127 private static final String pathToStores = "../etc"; 128 private static final String keyStoreFile = "keystore"; 129 private static final String trustStoreFile = "truststore"; 130 private static final String passwd = "passphrase"; 131 private static String keyFilename = 132 System.getProperty("test.src", ".") + "/" + pathToStores 133 + "/" + keyStoreFile; 134 private static String trustFilename = 135 System.getProperty("test.src", ".") + "/" + pathToStores 136 + "/" + trustStoreFile; 137 138 /* 139 * Main entry point for this test. 140 */ 141 public static void main(String args[]) throws Exception { 142 // reset security properties to make sure that the algorithms 143 // and keys used in this test are not disabled. 144 Security.setProperty("jdk.tls.disabledAlgorithms", ""); 145 Security.setProperty("jdk.certpath.disabledAlgorithms", ""); 146 147 if (debug) { 148 System.setProperty("javax.net.debug", "all"); 149 } 150 151 String [] protocols = new String [] { 152 "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2" }; 153 154 for (String protocol : protocols) { 155 log("Testing " + protocol); 156 /* 157 * Run the tests with direct and indirect buffers. 158 */ 159 SSLSocketSSLEngineTemplate test = 160 new SSLSocketSSLEngineTemplate(protocol); 161 log("-------------------------------------"); 162 log("Testing " + protocol + " for direct buffers ..."); 163 test.runTest(true); 164 165 log("---------------------------------------"); 166 log("Testing " + protocol + " for indirect buffers ..."); 167 test.runTest(false); 168 } 169 170 System.out.println("Test Passed."); 171 } 172 173 /* 174 * Create an initialized SSLContext to use for these tests. 175 */ 176 public SSLSocketSSLEngineTemplate(String protocol) throws Exception { 177 178 KeyStore ks = KeyStore.getInstance("JKS"); 179 KeyStore ts = KeyStore.getInstance("JKS"); 180 181 char[] passphrase = "passphrase".toCharArray(); 182 183 ks.load(new FileInputStream(keyFilename), passphrase); 184 ts.load(new FileInputStream(trustFilename), passphrase); 185 186 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 187 kmf.init(ks, passphrase); 188 189 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 190 tmf.init(ts); 191 192 SSLContext sslCtx = SSLContext.getInstance(protocol); 193 194 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 195 196 sslc = sslCtx; 197 } 198 199 /* 200 * Run the test. 201 * 202 * Sit in a tight loop, with the server engine calling wrap/unwrap 203 * regardless of whether data is available or not. We do this until 204 * we get the application data. Then we shutdown and go to the next one. 205 * 206 * The main loop handles all of the I/O phases of the SSLEngine's 207 * lifetime: 208 * 209 * initial handshaking 210 * application data transfer 211 * engine closing 212 * 213 * One could easily separate these phases into separate 214 * sections of code. 215 */ 216 private void runTest(boolean direct) throws Exception { 217 boolean serverClose = direct; 218 219 serverSocket = new ServerSocket(); 220 serverSocket.setReuseAddress(false); 221 serverSocket.bind(null); 222 int port = serverSocket.getLocalPort(); 223 Thread thread = createClientThread(port, serverClose); 224 225 socket = serverSocket.accept(); 226 socket.setSoTimeout(500); 227 serverSocket.close(); 228 229 createSSLEngine(); 230 createBuffers(direct); 231 232 try { 233 boolean closed = false; 234 // will try to read one more time in case client message 235 // is fragmented to multiple pieces 236 boolean retry = true; 237 238 InputStream is = socket.getInputStream(); 239 OutputStream os = socket.getOutputStream(); 240 241 SSLEngineResult serverResult; // results from last operation 242 243 /* 244 * Examining the SSLEngineResults could be much more involved, 245 * and may alter the overall flow of the application. 246 * 247 * For example, if we received a BUFFER_OVERFLOW when trying 248 * to write to the output pipe, we could reallocate a larger 249 * pipe, but instead we wait for the peer to drain it. 250 */ 251 byte[] inbound = new byte[8192]; 252 byte[] outbound = new byte[8192]; 253 254 while (!isEngineClosed(serverEngine)) { 255 int len = 0; 256 257 // Inbound data 258 log("================"); 259 260 // Read from the Client side. 261 try { 262 len = is.read(inbound); 263 if (len == -1) { 264 throw new Exception("Unexpected EOF"); 265 } 266 cTOs.put(inbound, 0, len); 267 } catch (SocketTimeoutException ste) { 268 // swallow. Nothing yet, probably waiting on us. 269 } 270 271 cTOs.flip(); 272 273 serverResult = serverEngine.unwrap(cTOs, serverIn); 274 log("server unwrap: ", serverResult); 275 runDelegatedTasks(serverResult, serverEngine); 276 cTOs.compact(); 277 278 // Outbound data 279 log("----"); 280 281 serverResult = serverEngine.wrap(serverOut, sTOc); 282 log("server wrap: ", serverResult); 283 runDelegatedTasks(serverResult, serverEngine); 284 285 sTOc.flip(); 286 287 if ((len = sTOc.remaining()) != 0) { 288 sTOc.get(outbound, 0, len); 289 os.write(outbound, 0, len); 290 // Give the other side a chance to process 291 } 292 293 sTOc.compact(); 294 295 if (!closed && (serverOut.remaining() == 0)) { 296 closed = true; 297 298 /* 299 * We'll alternate initiatating the shutdown. 300 * When the server initiates, it will take one more 301 * loop, but tests the orderly shutdown. 302 */ 303 if (serverClose) { 304 serverEngine.closeOutbound(); 305 } 306 serverIn.flip(); 307 308 /* 309 * A sanity check to ensure we got what was sent. 310 */ 311 if (serverIn.remaining() != clientMsg.length) { 312 if (retry && serverIn.remaining() < clientMsg.length) { 313 log("Need to read more from client"); 314 retry = false; 315 continue; 316 } else { 317 throw new Exception("Client: Data length error"); 318 } 319 } 320 321 for (int i = 0; i < clientMsg.length; i++) { 322 if (clientMsg[i] != serverIn.get()) { 323 throw new Exception("Client: Data content error"); 324 } 325 } 326 serverIn.compact(); 327 } 328 } 329 return; 330 } catch (Exception e) { 331 serverException = e; 332 } finally { 333 if (socket != null) { 334 socket.close(); 335 } 336 337 // Wait for the client to join up with us. 338 if (thread != null) { 339 thread.join(); 340 } 341 342 if (sslSocket != null) { 343 sslSocket.close(); 344 } 345 346 if (serverException != null) { 347 if (clientException != null) { 348 serverException.initCause(clientException); 349 } 350 throw serverException; 351 } 352 if (clientException != null) { 353 if (serverException != null) { 354 clientException.initCause(serverException); 355 } 356 throw clientException; 357 } 358 } 359 } 360 361 /* 362 * Create a client thread which does simple SSLSocket operations. 363 * We'll write and read one data packet. 364 */ 365 private Thread createClientThread(final int port, 366 final boolean serverClose) throws Exception { 367 368 Thread t = new Thread("ClientThread") { 369 370 @Override 371 public void run() { 372 try { 373 Thread.sleep(1000); // Give server time to finish setup. 374 375 sslSocket = (SSLSocket) sslc.getSocketFactory(). 376 createSocket("localhost", port); 377 OutputStream os = sslSocket.getOutputStream(); 378 InputStream is = sslSocket.getInputStream(); 379 380 // write(byte[]) goes in one shot. 381 os.write(clientMsg); 382 383 byte[] inbound = new byte[2048]; 384 int pos = 0; 385 386 int len; 387 done: 388 while ((len = is.read(inbound, pos, 2048 - pos)) != -1) { 389 pos += len; 390 // Let the client do the closing. 391 if ((pos == serverMsg.length) && !serverClose) { 392 sslSocket.close(); 393 break done; 394 } 395 } 396 397 if (pos != serverMsg.length) { 398 throw new Exception("Client: Data length error"); 399 } 400 401 for (int i = 0; i < serverMsg.length; i++) { 402 if (inbound[i] != serverMsg[i]) { 403 throw new Exception("Client: Data content error"); 404 } 405 } 406 } catch (Exception e) { 407 clientException = e; 408 } 409 } 410 }; 411 t.start(); 412 return t; 413 } 414 415 /* 416 * Using the SSLContext created during object creation, 417 * create/configure the SSLEngines we'll use for this test. 418 */ 419 private void createSSLEngine() throws Exception { 420 /* 421 * Configure the serverEngine to act as a server in the SSL/TLS 422 * handshake. 423 */ 424 serverEngine = sslc.createSSLEngine(); 425 serverEngine.setUseClientMode(false); 426 serverEngine.getNeedClientAuth(); 427 } 428 429 /* 430 * Create and size the buffers appropriately. 431 */ 432 private void createBuffers(boolean direct) { 433 434 SSLSession session = serverEngine.getSession(); 435 int appBufferMax = session.getApplicationBufferSize(); 436 int netBufferMax = session.getPacketBufferSize(); 437 438 /* 439 * We'll make the input buffers a bit bigger than the max needed 440 * size, so that unwrap()s following a successful data transfer 441 * won't generate BUFFER_OVERFLOWS. 442 * 443 * We'll use a mix of direct and indirect ByteBuffers for 444 * tutorial purposes only. In reality, only use direct 445 * ByteBuffers when they give a clear performance enhancement. 446 */ 447 if (direct) { 448 serverIn = ByteBuffer.allocateDirect(appBufferMax + 50); 449 cTOs = ByteBuffer.allocateDirect(netBufferMax); 450 sTOc = ByteBuffer.allocateDirect(netBufferMax); 451 } else { 452 serverIn = ByteBuffer.allocate(appBufferMax + 50); 453 cTOs = ByteBuffer.allocate(netBufferMax); 454 sTOc = ByteBuffer.allocate(netBufferMax); 455 } 456 457 serverOut = ByteBuffer.wrap(serverMsg); 458 } 459 460 /* 461 * If the result indicates that we have outstanding tasks to do, 462 * go ahead and run them in this thread. 463 */ 464 private static void runDelegatedTasks(SSLEngineResult result, 465 SSLEngine engine) throws Exception { 466 467 if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { 468 Runnable runnable; 469 while ((runnable = engine.getDelegatedTask()) != null) { 470 log("\trunning delegated task..."); 471 runnable.run(); 472 } 473 HandshakeStatus hsStatus = engine.getHandshakeStatus(); 474 if (hsStatus == HandshakeStatus.NEED_TASK) { 475 throw new Exception( 476 "handshake shouldn't need additional tasks"); 477 } 478 log("\tnew HandshakeStatus: " + hsStatus); 479 } 480 } 481 482 private static boolean isEngineClosed(SSLEngine engine) { 483 return (engine.isOutboundDone() && engine.isInboundDone()); 484 } 485 486 /* 487 * Logging code 488 */ 489 private static boolean resultOnce = true; 490 491 private static void log(String str, SSLEngineResult result) { 492 if (!logging) { 493 return; 494 } 495 if (resultOnce) { 496 resultOnce = false; 497 System.out.println("The format of the SSLEngineResult is: \n" 498 + "\t\"getStatus() / getHandshakeStatus()\" +\n" 499 + "\t\"bytesConsumed() / bytesProduced()\"\n"); 500 } 501 HandshakeStatus hsStatus = result.getHandshakeStatus(); 502 log(str 503 + result.getStatus() + "/" + hsStatus + ", " 504 + result.bytesConsumed() + "/" + result.bytesProduced() 505 + " bytes"); 506 if (hsStatus == HandshakeStatus.FINISHED) { 507 log("\t...ready for application data"); 508 } 509 } 510 511 private static void log(String str) { 512 if (logging) { 513 System.out.println(str); 514 } 515 } 516 }