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