1 /* 2 * Copyright (c) 2003, 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 * test 26 * @bug 7126889 27 * @summary Incorrect SSLEngine debug output 28 * 29 * Debug output was reporting n+1 bytes of data was written when it was 30 * really was n. 31 * 32 * SunJSSE does not support dynamic system properties, no way to re-use 33 * system properties in samevm/agentvm mode. 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 72 import javax.net.ssl.*; 73 import javax.net.ssl.SSLEngineResult.*; 74 import java.io.*; 75 import java.security.*; 76 import java.nio.*; 77 78 public class DebugReportsOneExtraByte { 79 80 /* 81 * Enables logging of the SSLEngine operations. 82 */ 83 private static boolean logging = true; 84 85 /* 86 * Enables the JSSE system debugging system property: 87 * 88 * -Djavax.net.debug=all 89 * 90 * This gives a lot of low-level information about operations underway, 91 * including specific handshake messages, and might be best examined 92 * after gaining some familiarity with this application. 93 */ 94 private static boolean debug = false; 95 96 private SSLContext sslc; 97 98 private SSLEngine clientEngine; // client Engine 99 private ByteBuffer clientOut; // write side of clientEngine 100 private ByteBuffer clientIn; // read side of clientEngine 101 102 private SSLEngine serverEngine; // server Engine 103 private ByteBuffer serverOut; // write side of serverEngine 104 private ByteBuffer serverIn; // read side of serverEngine 105 106 /* 107 * For data transport, this example uses local ByteBuffers. This 108 * isn't really useful, but the purpose of this example is to show 109 * SSLEngine concepts, not how to do network transport. 110 */ 111 private ByteBuffer cTOs; // "reliable" transport client->server 112 private ByteBuffer sTOc; // "reliable" transport server->client 113 114 /* 115 * The following is to set up the keystores. 116 */ 117 private static String pathToStores = "../../../../javax/net/ssl/etc"; 118 private static String keyStoreFile = "keystore"; 119 private static String trustStoreFile = "truststore"; 120 private static String passwd = "passphrase"; 121 122 private static String keyFilename = 123 System.getProperty("test.src", ".") + "/" + pathToStores + 124 "/" + keyStoreFile; 125 private static String trustFilename = 126 System.getProperty("test.src", ".") + "/" + pathToStores + 127 "/" + trustStoreFile; 128 129 /* 130 * Main entry point for this test. 131 */ 132 public static void main(String args[]) throws Exception { 133 if (debug) { 134 System.setProperty("javax.net.debug", "all"); 135 } 136 137 DebugReportsOneExtraByte test = new DebugReportsOneExtraByte(); 138 test.runTest(); 139 140 System.out.println("Test Passed."); 141 } 142 143 /* 144 * Create an initialized SSLContext to use for these tests. 145 */ 146 public DebugReportsOneExtraByte() throws Exception { 147 148 KeyStore ks = KeyStore.getInstance("JKS"); 149 KeyStore ts = KeyStore.getInstance("JKS"); 150 151 char[] passphrase = "passphrase".toCharArray(); 152 153 ks.load(new FileInputStream(keyFilename), passphrase); 154 ts.load(new FileInputStream(trustFilename), passphrase); 155 156 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 157 kmf.init(ks, passphrase); 158 159 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 160 tmf.init(ts); 161 162 SSLContext sslCtx = SSLContext.getInstance("TLSv1"); 163 164 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 165 166 sslc = sslCtx; 167 } 168 169 /* 170 * Run the test. 171 * 172 * Sit in a tight loop, both engines calling wrap/unwrap regardless 173 * of whether data is available or not. We do this until both engines 174 * report back they are closed. 175 * 176 * The main loop handles all of the I/O phases of the SSLEngine's 177 * lifetime: 178 * 179 * initial handshaking 180 * application data transfer 181 * engine closing 182 * 183 * One could easily separate these phases into separate 184 * sections of code. 185 */ 186 private void runTest() throws Exception { 187 boolean dataDone = false; 188 189 createSSLEngines(); 190 createBuffers(); 191 192 SSLEngineResult clientResult; // results from client's last operation 193 SSLEngineResult serverResult; // results from server's last operation 194 195 /* 196 * Examining the SSLEngineResults could be much more involved, 197 * and may alter the overall flow of the application. 198 * 199 * For example, if we received a BUFFER_OVERFLOW when trying 200 * to write to the output pipe, we could reallocate a larger 201 * pipe, but instead we wait for the peer to drain it. 202 */ 203 204 /* 205 * Write one byte in first application packet, the rest 206 * will come later. 207 */ 208 serverOut.limit(1); 209 210 while (!isEngineClosed(clientEngine) || 211 !isEngineClosed(serverEngine)) { 212 213 log("================"); 214 215 clientResult = clientEngine.wrap(clientOut, cTOs); 216 log("client wrap: ", clientResult); 217 runDelegatedTasks(clientResult, clientEngine); 218 219 serverResult = serverEngine.wrap(serverOut, sTOc); 220 log("server wrap: ", serverResult); 221 runDelegatedTasks(serverResult, serverEngine); 222 223 // Next wrap will split. 224 if (serverOut.position() == 1) { 225 serverOut.limit(serverOut.capacity()); 226 } 227 228 cTOs.flip(); 229 sTOc.flip(); 230 231 log("----"); 232 233 clientResult = clientEngine.unwrap(sTOc, clientIn); 234 log("client unwrap: ", clientResult); 235 runDelegatedTasks(clientResult, clientEngine); 236 237 serverResult = serverEngine.unwrap(cTOs, serverIn); 238 log("server unwrap: ", serverResult); 239 runDelegatedTasks(serverResult, serverEngine); 240 241 cTOs.compact(); 242 sTOc.compact(); 243 244 /* 245 * After we've transfered all application data between the client 246 * and server, we close the clientEngine's outbound stream. 247 * This generates a close_notify handshake message, which the 248 * server engine receives and responds by closing itself. 249 */ 250 if (!dataDone && (clientOut.limit() == serverIn.position()) && 251 (serverOut.limit() == clientIn.position())) { 252 253 /* 254 * A sanity check to ensure we got what was sent. 255 */ 256 checkTransfer(serverOut, clientIn); 257 checkTransfer(clientOut, serverIn); 258 259 log("\tClosing clientEngine's *OUTBOUND*..."); 260 clientEngine.closeOutbound(); 261 dataDone = true; 262 } 263 } 264 } 265 266 /* 267 * Using the SSLContext created during object creation, 268 * create/configure the SSLEngines we'll use for this test. 269 */ 270 private void createSSLEngines() throws Exception { 271 /* 272 * Configure the serverEngine to act as a server in the SSL/TLS 273 * handshake. Also, require SSL client authentication. 274 */ 275 serverEngine = sslc.createSSLEngine(); 276 serverEngine.setUseClientMode(false); 277 serverEngine.setNeedClientAuth(true); 278 279 // Force a block-oriented ciphersuite. 280 serverEngine.setEnabledCipherSuites( 281 new String [] {"TLS_RSA_WITH_AES_128_CBC_SHA"}); 282 283 /* 284 * Similar to above, but using client mode instead. 285 */ 286 clientEngine = sslc.createSSLEngine("client", 80); 287 clientEngine.setUseClientMode(true); 288 } 289 290 /* 291 * Create and size the buffers appropriately. 292 */ 293 private void createBuffers() { 294 295 /* 296 * We'll assume the buffer sizes are the same 297 * between client and server. 298 */ 299 SSLSession session = clientEngine.getSession(); 300 int appBufferMax = session.getApplicationBufferSize(); 301 int netBufferMax = session.getPacketBufferSize(); 302 303 /* 304 * We'll make the input buffers a bit bigger than the max needed 305 * size, so that unwrap()s following a successful data transfer 306 * won't generate BUFFER_OVERFLOWS. 307 * 308 * We'll use a mix of direct and indirect ByteBuffers for 309 * tutorial purposes only. In reality, only use direct 310 * ByteBuffers when they give a clear performance enhancement. 311 */ 312 clientIn = ByteBuffer.allocate(appBufferMax + 50); 313 serverIn = ByteBuffer.allocate(appBufferMax + 50); 314 315 cTOs = ByteBuffer.allocateDirect(netBufferMax); 316 sTOc = ByteBuffer.allocateDirect(netBufferMax); 317 318 // No need to write anything on the client side, it will 319 // just confuse the output. 320 clientOut = ByteBuffer.wrap("".getBytes()); 321 // 10 bytes long 322 serverOut = ByteBuffer.wrap("Hi Client!".getBytes()); 323 } 324 325 /* 326 * If the result indicates that we have outstanding tasks to do, 327 * go ahead and run them in this thread. 328 */ 329 private static void runDelegatedTasks(SSLEngineResult result, 330 SSLEngine engine) throws Exception { 331 332 if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { 333 Runnable runnable; 334 while ((runnable = engine.getDelegatedTask()) != null) { 335 log("\trunning delegated task..."); 336 runnable.run(); 337 } 338 HandshakeStatus hsStatus = engine.getHandshakeStatus(); 339 if (hsStatus == HandshakeStatus.NEED_TASK) { 340 throw new Exception( 341 "handshake shouldn't need additional tasks"); 342 } 343 log("\tnew HandshakeStatus: " + hsStatus); 344 } 345 } 346 347 private static boolean isEngineClosed(SSLEngine engine) { 348 return (engine.isOutboundDone() && engine.isInboundDone()); 349 } 350 351 /* 352 * Simple check to make sure everything came across as expected. 353 */ 354 private static void checkTransfer(ByteBuffer a, ByteBuffer b) 355 throws Exception { 356 a.flip(); 357 b.flip(); 358 359 if (!a.equals(b)) { 360 throw new Exception("Data didn't transfer cleanly"); 361 } else { 362 log("\tData transferred cleanly"); 363 } 364 365 a.position(a.limit()); 366 b.position(b.limit()); 367 a.limit(a.capacity()); 368 b.limit(b.capacity()); 369 } 370 371 /* 372 * Logging code 373 */ 374 private static boolean resultOnce = true; 375 376 private static void log(String str, SSLEngineResult result) { 377 if (!logging) { 378 return; 379 } 380 if (resultOnce) { 381 resultOnce = false; 382 System.out.println("The format of the SSLEngineResult is: \n" + 383 "\t\"getStatus() / getHandshakeStatus()\" +\n" + 384 "\t\"bytesConsumed() / bytesProduced()\"\n"); 385 } 386 HandshakeStatus hsStatus = result.getHandshakeStatus(); 387 log(str + 388 result.getStatus() + "/" + hsStatus + ", " + 389 result.bytesConsumed() + "/" + result.bytesProduced() + 390 " bytes"); 391 if (hsStatus == HandshakeStatus.FINISHED) { 392 log("\t...ready for application data"); 393 } 394 } 395 396 private static void log(String str) { 397 if (logging) { 398 System.out.println(str); 399 } 400 } 401 }