1 /* 2 * Copyright (c) 2006, 2012, 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 * @bug 6388456 26 * @summary Need adjustable TLS max record size for interoperability 27 * with non-compliant stacks 28 * 29 * Helper class of SSL/TLS client/server communication. 30 * 31 * @author Xuelei Fan 32 */ 33 34 import javax.net.ssl.*; 35 36 import java.io.*; 37 import java.security.*; 38 import java.nio.*; 39 import java.nio.channels.*; 40 41 public class SSLEngineService { 42 43 private static String keyStoreFile = "keystore"; 44 private static String trustStoreFile = "truststore"; 45 private static char[] passphrase = "passphrase".toCharArray(); 46 47 private String pathToStores; 48 private String keyFilename; 49 private String trustFilename; 50 SSLEngineService()51 protected SSLEngineService() { 52 init("../etc"); 53 } 54 SSLEngineService(String pathToStores)55 protected SSLEngineService(String pathToStores) { 56 init(pathToStores); 57 } 58 init(String pathToStores)59 private void init(String pathToStores) { 60 this.pathToStores = pathToStores; 61 this.keyFilename = 62 System.getProperty("test.src", "./") + "/" + pathToStores + 63 "/" + keyStoreFile; 64 this.trustFilename = 65 System.getProperty("test.src", "./") + "/" + pathToStores + 66 "/" + trustStoreFile; 67 } 68 69 // deliver local application data. deliver(SSLEngine ssle, SocketChannel sc)70 protected static void deliver(SSLEngine ssle, SocketChannel sc) 71 throws Exception { 72 73 // create buufer. 74 int appBufferMax = ssle.getSession().getApplicationBufferSize(); 75 int netBufferMax = ssle.getSession().getPacketBufferSize(); 76 int length = appBufferMax * (Integer.SIZE / 8); 77 78 // allocate more in order to check large packet 79 ByteBuffer localAppData = ByteBuffer.allocate(length); 80 81 // allocate less in order to check BUFFER_OVERFLOW/BUFFER_UNDERFLOW 82 ByteBuffer localNetData = ByteBuffer.allocate(netBufferMax/2); 83 84 // prepare local application data 85 localAppData.putInt(length); 86 for (int i = 1; i < appBufferMax; i++) { 87 localAppData.putInt(i); 88 } 89 localAppData.flip(); 90 91 92 while (localAppData.hasRemaining()) { 93 // empty the local network packet buffer. 94 localNetData.clear(); 95 96 // generated local network packet. 97 SSLEngineResult res = ssle.wrap(localAppData, localNetData); 98 99 // checking status 100 switch (res.getStatus()) { 101 102 case OK : 103 localNetData.flip(); 104 105 // send the network packet 106 while (localNetData.hasRemaining()) { 107 if (sc.write(localNetData) < 0) { 108 throw new IOException("Unable write to socket channel"); 109 } 110 } 111 112 if (res.getHandshakeStatus() == 113 SSLEngineResult.HandshakeStatus.NEED_TASK) { 114 Runnable runnable; 115 while ((runnable = ssle.getDelegatedTask()) != null) { 116 runnable.run(); 117 } 118 } 119 120 // detect large buffer 121 if (res.bytesProduced() >= Short.MAX_VALUE) { 122 System.out.println("Generate a " + 123 res.bytesProduced() + " bytes large packet "); 124 } 125 break; 126 127 case BUFFER_OVERFLOW : 128 // maybe need to enlarge the local network packet buffer. 129 int size = ssle.getSession().getPacketBufferSize(); 130 if (size > localNetData.capacity()) { 131 System.out.println("resize destination buffer upto " + 132 size + " bytes for BUFFER_OVERFLOW"); 133 localNetData = enlargeBuffer(localNetData, size); 134 } 135 break; 136 137 default : // BUFFER_UNDERFLOW or CLOSED : 138 throw new IOException("Received invalid" + res.getStatus() + 139 "during transfer application data"); 140 } 141 } 142 } 143 144 145 // receive peer application data. receive(SSLEngine ssle, SocketChannel sc)146 protected static void receive(SSLEngine ssle, SocketChannel sc) 147 throws Exception { 148 149 // create buufers. 150 int appBufferMax = ssle.getSession().getApplicationBufferSize(); 151 int netBufferMax = ssle.getSession().getPacketBufferSize(); 152 153 // allocate less in order to check BUFFER_OVERFLOW/BUFFER_UNDERFLOW 154 ByteBuffer peerAppData = ByteBuffer.allocate(appBufferMax/2); 155 ByteBuffer peerNetData = ByteBuffer.allocate(netBufferMax/2); 156 int received = -1; 157 158 boolean needToReadMore = true; 159 while (received != 0) { 160 if (needToReadMore) { 161 if (ssle.isInboundDone() || sc.read(peerNetData) < 0) { 162 break; 163 } 164 } 165 166 peerNetData.flip(); 167 SSLEngineResult res = ssle.unwrap(peerNetData, peerAppData); 168 peerNetData.compact(); 169 170 // checking status 171 switch (res.getStatus()) { 172 173 case OK : 174 if (res.getHandshakeStatus() == 175 SSLEngineResult.HandshakeStatus.NEED_TASK) { 176 Runnable runnable; 177 while ((runnable = ssle.getDelegatedTask()) != null) { 178 runnable.run(); 179 } 180 } 181 182 if (received < 0 && res.bytesProduced() < 4 ) { 183 break; 184 } 185 186 if (received < 0) { 187 received = peerAppData.getInt(0); 188 } 189 190 System.out.println("received " + peerAppData.position() + 191 " bytes client application data"); 192 System.out.println("\tcomsumed " + res.bytesConsumed() + 193 " byes network data"); 194 peerAppData.clear(); 195 196 received -= res.bytesProduced(); 197 198 // detect large buffer 199 if (res.bytesConsumed() >= Short.MAX_VALUE) { 200 System.out.println("Consumes a " + res.bytesConsumed() + 201 " bytes large packet "); 202 } 203 204 needToReadMore = (peerNetData.position() > 0) ? false : true; 205 206 break; 207 208 case BUFFER_OVERFLOW : 209 // maybe need to enlarge the peer application data buffer. 210 int size = ssle.getSession().getApplicationBufferSize(); 211 if (size > peerAppData.capacity()) { 212 System.out.println("resize destination buffer upto " + 213 size + " bytes for BUFFER_OVERFLOW"); 214 peerAppData = enlargeBuffer(peerAppData, size); 215 } 216 break; 217 218 case BUFFER_UNDERFLOW : 219 // maybe need to enlarge the peer network packet data buffer. 220 size = ssle.getSession().getPacketBufferSize(); 221 if (size > peerNetData.capacity()) { 222 System.out.println("resize source buffer upto " + size + 223 " bytes for BUFFER_UNDERFLOW"); 224 peerNetData = enlargeBuffer(peerNetData, size); 225 } 226 227 needToReadMore = true; 228 break; 229 230 default : // CLOSED : 231 throw new IOException("Received invalid" + res.getStatus() + 232 "during transfer application data"); 233 } 234 } 235 } 236 handshaking(SSLEngine ssle, SocketChannel sc, ByteBuffer additional)237 protected static void handshaking(SSLEngine ssle, SocketChannel sc, 238 ByteBuffer additional) throws Exception { 239 240 int appBufferMax = ssle.getSession().getApplicationBufferSize(); 241 int netBufferMax = ssle.getSession().getPacketBufferSize(); 242 243 // allocate less in order to check BUFFER_OVERFLOW/BUFFER_UNDERFLOW 244 ByteBuffer localAppData = ByteBuffer.allocate(appBufferMax/10); 245 ByteBuffer peerAppData = ByteBuffer.allocate(appBufferMax/10); 246 ByteBuffer localNetData = ByteBuffer.allocate(netBufferMax/10); 247 ByteBuffer peerNetData = ByteBuffer.allocate(netBufferMax/10); 248 249 // begin handshake 250 ssle.beginHandshake(); 251 SSLEngineResult.HandshakeStatus hs = ssle.getHandshakeStatus(); 252 253 // start handshaking from unwrap 254 byte[] buffer = new byte[0xFF]; 255 boolean underflow = false; 256 do { 257 switch (hs) { 258 259 case NEED_UNWRAP : 260 if (peerNetData.position() == 0) { 261 if (additional != null && additional.hasRemaining()) { 262 do { 263 int len = Math.min(buffer.length, 264 peerNetData.remaining()); 265 len = Math.min(len, additional.remaining()); 266 if (len != 0) { 267 additional.get(buffer, 0, len); 268 peerNetData.put(buffer, 0, len); 269 } 270 } while (peerNetData.remaining() > 0 && 271 additional.hasRemaining()); 272 } else { 273 if (sc.read(peerNetData) < 0) { 274 ssle.closeInbound(); 275 return; 276 } 277 } 278 } 279 280 if (underflow) { 281 if (sc.read(peerNetData) < 0) { 282 ssle.closeInbound(); 283 return; 284 } 285 286 underflow = false; 287 } 288 289 peerNetData.flip(); 290 SSLEngineResult res = ssle.unwrap(peerNetData, peerAppData); 291 peerNetData.compact(); 292 hs = res.getHandshakeStatus(); 293 294 switch (res.getStatus()) { 295 case OK : 296 break; 297 case BUFFER_UNDERFLOW : 298 // maybe need to enlarge the peer network packet buffer. 299 int size = ssle.getSession().getPacketBufferSize(); 300 if (size > peerNetData.capacity()) { 301 System.out.println("resize source buffer upto " + 302 size + " bytes for BUFFER_UNDERFLOW"); 303 peerNetData = enlargeBuffer(peerNetData, size); 304 } 305 306 underflow = true; 307 break; 308 case BUFFER_OVERFLOW : 309 // maybe need to enlarge the peer application data buffer. 310 size = ssle.getSession().getApplicationBufferSize(); 311 if (size > peerAppData.capacity()) { 312 System.out.println("resize destination buffer upto " + 313 size + " bytes for BUFFER_OVERFLOW"); 314 peerAppData = enlargeBuffer(peerAppData, size); 315 } 316 break; 317 default : //CLOSED 318 throw new IOException("Received invalid" + res.getStatus() + 319 "during initial handshaking"); 320 } 321 break; 322 323 case NEED_WRAP : 324 // empty the local network packet buffer. 325 localNetData.clear(); 326 327 // generated local network packet. 328 res = ssle.wrap(localAppData, localNetData); 329 hs = res.getHandshakeStatus(); 330 331 // checking status 332 switch (res.getStatus()) { 333 case OK : 334 localNetData.flip(); 335 336 // send the network packet 337 while (localNetData.hasRemaining()) { 338 if (sc.write(localNetData) < 0) { 339 throw new IOException( 340 "Unable write to socket channel"); 341 } 342 } 343 break; 344 345 case BUFFER_OVERFLOW : 346 // maybe need to enlarge the local network packet buffer. 347 int size = ssle.getSession().getPacketBufferSize(); 348 if (size > localNetData.capacity()) { 349 System.out.println("resize destination buffer upto " + 350 size + " bytes for BUFFER_OVERFLOW"); 351 localNetData = enlargeBuffer(localNetData, size); 352 } 353 break; 354 355 default : // BUFFER_UNDERFLOW or CLOSED : 356 throw new IOException("Received invalid" + res.getStatus() + 357 "during initial handshaking"); 358 } 359 break; 360 361 case NEED_TASK : 362 Runnable runnable; 363 while ((runnable = ssle.getDelegatedTask()) != null) { 364 runnable.run(); 365 } 366 hs = ssle.getHandshakeStatus(); 367 break; 368 369 default : // FINISHED or NOT_HANDSHAKING 370 // do nothing 371 } 372 } while (hs != SSLEngineResult.HandshakeStatus.FINISHED && 373 hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING); 374 } 375 enlargeBuffer(ByteBuffer buffer, int size)376 private static ByteBuffer enlargeBuffer(ByteBuffer buffer, int size) { 377 ByteBuffer bb = ByteBuffer.allocate(size); 378 buffer.flip(); 379 bb.put(buffer); 380 381 return bb; 382 } 383 384 /* 385 * Create an initialized SSLContext to use for this test. 386 */ createSSLEngine(boolean mode)387 protected SSLEngine createSSLEngine(boolean mode) throws Exception { 388 389 SSLEngine ssle; 390 391 KeyStore ks = KeyStore.getInstance("JKS"); 392 KeyStore ts = KeyStore.getInstance("JKS"); 393 394 ks.load(new FileInputStream(keyFilename), passphrase); 395 ts.load(new FileInputStream(trustFilename), passphrase); 396 397 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 398 kmf.init(ks, passphrase); 399 400 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 401 tmf.init(ts); 402 403 SSLContext sslCtx = SSLContext.getInstance("TLS"); 404 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 405 406 ssle = sslCtx.createSSLEngine(); 407 ssle.setUseClientMode(mode); 408 409 return ssle; 410 } 411 } 412