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