1 /* 2 * Copyright (c) 2015, 2016, 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 8043758 30 * @summary Datagram Transport Layer Security (DTLS) 31 * @modules java.base/sun.security.util 32 * @run main/othervm DTLSOverDatagram 33 */ 34 35 import java.io.*; 36 import java.nio.*; 37 import java.net.*; 38 import java.util.*; 39 import java.security.*; 40 import java.security.cert.*; 41 import javax.net.ssl.*; 42 import java.util.concurrent.*; 43 44 import sun.security.util.HexDumpEncoder; 45 46 /** 47 * An example to show the way to use SSLEngine in datagram connections. 48 */ 49 public class DTLSOverDatagram { 50 51 static { 52 System.setProperty("javax.net.debug", "ssl"); 53 } 54 55 private static int MAX_HANDSHAKE_LOOPS = 200; 56 private static int MAX_APP_READ_LOOPS = 60; 57 private static int SOCKET_TIMEOUT = 10 * 1000; // in millis 58 private static int BUFFER_SIZE = 1024; 59 private static int MAXIMUM_PACKET_SIZE = 1024; 60 61 /* 62 * The following is to set up the keystores. 63 */ 64 private static String pathToStores = "../etc"; 65 private static String keyStoreFile = "keystore"; 66 private static String trustStoreFile = "truststore"; 67 private static String passwd = "passphrase"; 68 69 private static String keyFilename = 70 System.getProperty("test.src", ".") + "/" + pathToStores + 71 "/" + keyStoreFile; 72 private static String trustFilename = 73 System.getProperty("test.src", ".") + "/" + pathToStores + 74 "/" + trustStoreFile; 75 private static Exception clientException = null; 76 private static Exception serverException = null; 77 78 private static ByteBuffer serverApp = 79 ByteBuffer.wrap("Hi Client, I'm Server".getBytes()); 80 private static ByteBuffer clientApp = 81 ByteBuffer.wrap("Hi Server, I'm Client".getBytes()); 82 83 /* 84 * ============================================================= 85 * The test case 86 */ 87 public static void main(String[] args) throws Exception { 88 DTLSOverDatagram testCase = new DTLSOverDatagram(); 89 testCase.runTest(testCase); 90 } 91 92 /* 93 * Define the server side of the test. 94 */ 95 void doServerSide(DatagramSocket socket, InetSocketAddress clientSocketAddr) 96 throws Exception { 97 98 // create SSLEngine 99 SSLEngine engine = createSSLEngine(false); 100 101 // handshaking 102 handshake(engine, socket, clientSocketAddr, "Server"); 103 104 // read client application data 105 receiveAppData(engine, socket, clientApp); 106 107 // write server application data 108 deliverAppData(engine, socket, serverApp, clientSocketAddr); 109 } 110 111 /* 112 * Define the client side of the test. 113 */ 114 void doClientSide(DatagramSocket socket, InetSocketAddress serverSocketAddr) 115 throws Exception { 116 117 // create SSLEngine 118 SSLEngine engine = createSSLEngine(true); 119 120 // handshaking 121 handshake(engine, socket, serverSocketAddr, "Client"); 122 123 // write client application data 124 deliverAppData(engine, socket, clientApp, serverSocketAddr); 125 126 // read server application data 127 receiveAppData(engine, socket, serverApp); 128 } 129 130 /* 131 * ============================================================= 132 * The remainder is support stuff for DTLS operations. 133 */ 134 SSLEngine createSSLEngine(boolean isClient) throws Exception { 135 SSLContext context = getDTLSContext(); 136 SSLEngine engine = context.createSSLEngine(); 137 138 SSLParameters paras = engine.getSSLParameters(); 139 paras.setMaximumPacketSize(MAXIMUM_PACKET_SIZE); 140 141 engine.setUseClientMode(isClient); 142 engine.setSSLParameters(paras); 143 144 return engine; 145 } 146 147 // handshake 148 void handshake(SSLEngine engine, DatagramSocket socket, 149 SocketAddress peerAddr, String side) throws Exception { 150 151 boolean endLoops = false; 152 int loops = MAX_HANDSHAKE_LOOPS; 153 engine.beginHandshake(); 154 while (!endLoops && 155 (serverException == null) && (clientException == null)) { 156 157 if (--loops < 0) { 158 throw new RuntimeException( 159 "Too much loops to produce handshake packets"); 160 } 161 162 SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); 163 if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP || 164 hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) { 165 166 log(side, "Receive DTLS records, handshake status is " + hs); 167 168 ByteBuffer iNet; 169 ByteBuffer iApp; 170 if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) { 171 byte[] buf = new byte[BUFFER_SIZE]; 172 DatagramPacket packet = new DatagramPacket(buf, buf.length); 173 try { 174 socket.receive(packet); 175 } catch (SocketTimeoutException ste) { 176 log(side, "Warning: " + ste); 177 178 List<DatagramPacket> packets = new ArrayList<>(); 179 boolean finished = onReceiveTimeout( 180 engine, peerAddr, side, packets); 181 182 for (DatagramPacket p : packets) { 183 socket.send(p); 184 } 185 186 if (finished) { 187 log(side, "Handshake status is FINISHED " 188 + "after calling onReceiveTimeout(), " 189 + "finish the loop"); 190 endLoops = true; 191 } 192 193 log(side, "New handshake status is " 194 + engine.getHandshakeStatus()); 195 196 continue; 197 } 198 199 iNet = ByteBuffer.wrap(buf, 0, packet.getLength()); 200 iApp = ByteBuffer.allocate(BUFFER_SIZE); 201 } else { 202 iNet = ByteBuffer.allocate(0); 203 iApp = ByteBuffer.allocate(BUFFER_SIZE); 204 } 205 206 SSLEngineResult r = engine.unwrap(iNet, iApp); 207 SSLEngineResult.Status rs = r.getStatus(); 208 hs = r.getHandshakeStatus(); 209 if (rs == SSLEngineResult.Status.OK) { 210 // OK 211 } else if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) { 212 log(side, "BUFFER_OVERFLOW, handshake status is " + hs); 213 214 // the client maximum fragment size config does not work? 215 throw new Exception("Buffer overflow: " + 216 "incorrect client maximum fragment size"); 217 } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) { 218 log(side, "BUFFER_UNDERFLOW, handshake status is " + hs); 219 220 // bad packet, or the client maximum fragment size 221 // config does not work? 222 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { 223 throw new Exception("Buffer underflow: " + 224 "incorrect client maximum fragment size"); 225 } // otherwise, ignore this packet 226 } else if (rs == SSLEngineResult.Status.CLOSED) { 227 throw new Exception( 228 "SSL engine closed, handshake status is " + hs); 229 } else { 230 throw new Exception("Can't reach here, result is " + rs); 231 } 232 233 if (hs == SSLEngineResult.HandshakeStatus.FINISHED) { 234 log(side, "Handshake status is FINISHED, finish the loop"); 235 endLoops = true; 236 } 237 } else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) { 238 List<DatagramPacket> packets = new ArrayList<>(); 239 boolean finished = produceHandshakePackets( 240 engine, peerAddr, side, packets); 241 242 for (DatagramPacket p : packets) { 243 socket.send(p); 244 } 245 246 if (finished) { 247 log(side, "Handshake status is FINISHED " 248 + "after producing handshake packets, " 249 + "finish the loop"); 250 endLoops = true; 251 } 252 } else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) { 253 runDelegatedTasks(engine); 254 } else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { 255 log(side, "Handshake status is NOT_HANDSHAKING, finish the loop"); 256 endLoops = true; 257 } else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) { 258 throw new Exception( 259 "Unexpected status, SSLEngine.getHandshakeStatus() " 260 + "shouldn't return FINISHED"); 261 } else { 262 throw new Exception("Can't reach here, handshake status is " + hs); 263 } 264 } 265 266 SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); 267 log(side, "Handshake finished, status is " + hs); 268 269 if (engine.getHandshakeSession() != null) { 270 throw new Exception( 271 "Handshake finished, but handshake session is not null"); 272 } 273 274 SSLSession session = engine.getSession(); 275 if (session == null) { 276 throw new Exception("Handshake finished, but session is null"); 277 } 278 log(side, "Negotiated protocol is " + session.getProtocol()); 279 log(side, "Negotiated cipher suite is " + session.getCipherSuite()); 280 281 // handshake status should be NOT_HANDSHAKING 282 // according to the spec, SSLEngine.getHandshakeStatus() can't return FINISHED 283 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { 284 throw new Exception("Unexpected handshake status " + hs); 285 } 286 } 287 288 // deliver application data 289 void deliverAppData(SSLEngine engine, DatagramSocket socket, 290 ByteBuffer appData, SocketAddress peerAddr) throws Exception { 291 292 // Note: have not consider the packet loses 293 List<DatagramPacket> packets = 294 produceApplicationPackets(engine, appData, peerAddr); 295 appData.flip(); 296 for (DatagramPacket p : packets) { 297 socket.send(p); 298 } 299 } 300 301 // receive application data 302 void receiveAppData(SSLEngine engine, 303 DatagramSocket socket, ByteBuffer expectedApp) throws Exception { 304 305 int loops = MAX_APP_READ_LOOPS; 306 while ((serverException == null) && (clientException == null)) { 307 if (--loops < 0) { 308 throw new RuntimeException( 309 "Too much loops to receive application data"); 310 } 311 312 byte[] buf = new byte[BUFFER_SIZE]; 313 DatagramPacket packet = new DatagramPacket(buf, buf.length); 314 socket.receive(packet); 315 ByteBuffer netBuffer = ByteBuffer.wrap(buf, 0, packet.getLength()); 316 ByteBuffer recBuffer = ByteBuffer.allocate(BUFFER_SIZE); 317 SSLEngineResult rs = engine.unwrap(netBuffer, recBuffer); 318 recBuffer.flip(); 319 if (recBuffer.remaining() != 0) { 320 printHex("Received application data", recBuffer); 321 if (!recBuffer.equals(expectedApp)) { 322 System.out.println("Engine status is " + rs); 323 throw new Exception("Not the right application data"); 324 } 325 break; 326 } 327 } 328 } 329 330 // produce handshake packets 331 boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr, 332 String side, List<DatagramPacket> packets) throws Exception { 333 334 boolean endLoops = false; 335 int loops = MAX_HANDSHAKE_LOOPS; 336 while (!endLoops && 337 (serverException == null) && (clientException == null)) { 338 339 if (--loops < 0) { 340 throw new RuntimeException( 341 "Too much loops to produce handshake packets"); 342 } 343 344 ByteBuffer oNet = ByteBuffer.allocate(32768); 345 ByteBuffer oApp = ByteBuffer.allocate(0); 346 SSLEngineResult r = engine.wrap(oApp, oNet); 347 oNet.flip(); 348 349 SSLEngineResult.Status rs = r.getStatus(); 350 SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus(); 351 if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) { 352 // the client maximum fragment size config does not work? 353 throw new Exception("Buffer overflow: " + 354 "incorrect server maximum fragment size"); 355 } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) { 356 log(side, "Produce handshake packets: BUFFER_UNDERFLOW occured"); 357 log(side, "Produce handshake packets: Handshake status: " + hs); 358 // bad packet, or the client maximum fragment size 359 // config does not work? 360 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { 361 throw new Exception("Buffer underflow: " + 362 "incorrect server maximum fragment size"); 363 } // otherwise, ignore this packet 364 } else if (rs == SSLEngineResult.Status.CLOSED) { 365 throw new Exception("SSLEngine has closed"); 366 } else if (rs == SSLEngineResult.Status.OK) { 367 // OK 368 } else { 369 throw new Exception("Can't reach here, result is " + rs); 370 } 371 372 // SSLEngineResult.Status.OK: 373 if (oNet.hasRemaining()) { 374 byte[] ba = new byte[oNet.remaining()]; 375 oNet.get(ba); 376 DatagramPacket packet = createHandshakePacket(ba, socketAddr); 377 packets.add(packet); 378 } 379 380 if (hs == SSLEngineResult.HandshakeStatus.FINISHED) { 381 log(side, "Produce handshake packets: " 382 + "Handshake status is FINISHED, finish the loop"); 383 return true; 384 } 385 386 boolean endInnerLoop = false; 387 SSLEngineResult.HandshakeStatus nhs = hs; 388 while (!endInnerLoop) { 389 if (nhs == SSLEngineResult.HandshakeStatus.NEED_TASK) { 390 runDelegatedTasks(engine); 391 } else if (nhs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP || 392 nhs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN || 393 nhs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { 394 395 endInnerLoop = true; 396 endLoops = true; 397 } else if (nhs == SSLEngineResult.HandshakeStatus.NEED_WRAP) { 398 endInnerLoop = true; 399 } else if (nhs == SSLEngineResult.HandshakeStatus.FINISHED) { 400 throw new Exception( 401 "Unexpected status, SSLEngine.getHandshakeStatus() " 402 + "shouldn't return FINISHED"); 403 } else { 404 throw new Exception("Can't reach here, handshake status is " 405 + nhs); 406 } 407 nhs = engine.getHandshakeStatus(); 408 } 409 } 410 411 return false; 412 } 413 414 DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) { 415 return new DatagramPacket(ba, ba.length, socketAddr); 416 } 417 418 // produce application packets 419 List<DatagramPacket> produceApplicationPackets( 420 SSLEngine engine, ByteBuffer source, 421 SocketAddress socketAddr) throws Exception { 422 423 List<DatagramPacket> packets = new ArrayList<>(); 424 ByteBuffer appNet = ByteBuffer.allocate(32768); 425 SSLEngineResult r = engine.wrap(source, appNet); 426 appNet.flip(); 427 428 SSLEngineResult.Status rs = r.getStatus(); 429 if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) { 430 // the client maximum fragment size config does not work? 431 throw new Exception("Buffer overflow: " + 432 "incorrect server maximum fragment size"); 433 } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) { 434 // unlikely 435 throw new Exception("Buffer underflow during wraping"); 436 } else if (rs == SSLEngineResult.Status.CLOSED) { 437 throw new Exception("SSLEngine has closed"); 438 } else if (rs == SSLEngineResult.Status.OK) { 439 // OK 440 } else { 441 throw new Exception("Can't reach here, result is " + rs); 442 } 443 444 // SSLEngineResult.Status.OK: 445 if (appNet.hasRemaining()) { 446 byte[] ba = new byte[appNet.remaining()]; 447 appNet.get(ba); 448 DatagramPacket packet = 449 new DatagramPacket(ba, ba.length, socketAddr); 450 packets.add(packet); 451 } 452 453 return packets; 454 } 455 456 // run delegated tasks 457 void runDelegatedTasks(SSLEngine engine) throws Exception { 458 Runnable runnable; 459 while ((runnable = engine.getDelegatedTask()) != null) { 460 runnable.run(); 461 } 462 463 SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); 464 if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) { 465 throw new Exception("handshake shouldn't need additional tasks"); 466 } 467 } 468 469 // retransmission if timeout 470 boolean onReceiveTimeout(SSLEngine engine, SocketAddress socketAddr, 471 String side, List<DatagramPacket> packets) throws Exception { 472 473 SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); 474 if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { 475 return false; 476 } else { 477 // retransmission of handshake messages 478 return produceHandshakePackets(engine, socketAddr, side, packets); 479 } 480 } 481 482 // get DTSL context 483 SSLContext getDTLSContext() throws Exception { 484 KeyStore ks = KeyStore.getInstance("JKS"); 485 KeyStore ts = KeyStore.getInstance("JKS"); 486 487 char[] passphrase = "passphrase".toCharArray(); 488 489 try (FileInputStream fis = new FileInputStream(keyFilename)) { 490 ks.load(fis, passphrase); 491 } 492 493 try (FileInputStream fis = new FileInputStream(trustFilename)) { 494 ts.load(fis, passphrase); 495 } 496 497 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 498 kmf.init(ks, passphrase); 499 500 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 501 tmf.init(ts); 502 503 SSLContext sslCtx = SSLContext.getInstance("DTLS"); 504 505 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 506 507 return sslCtx; 508 } 509 510 511 /* 512 * ============================================================= 513 * The remainder is support stuff to kickstart the testing. 514 */ 515 516 // Will the handshaking and application data exchange succeed? 517 public boolean isGoodJob() { 518 return true; 519 } 520 521 public final void runTest(DTLSOverDatagram testCase) throws Exception { 522 try (DatagramSocket serverSocket = new DatagramSocket(); 523 DatagramSocket clientSocket = new DatagramSocket()) { 524 525 serverSocket.setSoTimeout(SOCKET_TIMEOUT); 526 clientSocket.setSoTimeout(SOCKET_TIMEOUT); 527 528 InetSocketAddress serverSocketAddr = new InetSocketAddress( 529 InetAddress.getLocalHost(), serverSocket.getLocalPort()); 530 531 InetSocketAddress clientSocketAddr = new InetSocketAddress( 532 InetAddress.getLocalHost(), clientSocket.getLocalPort()); 533 534 ExecutorService pool = Executors.newFixedThreadPool(2); 535 Future<String> server, client; 536 537 try { 538 server = pool.submit(new ServerCallable( 539 testCase, serverSocket, clientSocketAddr)); 540 client = pool.submit(new ClientCallable( 541 testCase, clientSocket, serverSocketAddr)); 542 } finally { 543 pool.shutdown(); 544 } 545 546 boolean failed = false; 547 548 // wait for client to finish 549 try { 550 System.out.println("Client finished: " + client.get()); 551 } catch (CancellationException | InterruptedException 552 | ExecutionException e) { 553 System.out.println("Exception on client side: "); 554 e.printStackTrace(System.out); 555 failed = true; 556 } 557 558 // wait for server to finish 559 try { 560 System.out.println("Client finished: " + server.get()); 561 } catch (CancellationException | InterruptedException 562 | ExecutionException e) { 563 System.out.println("Exception on server side: "); 564 e.printStackTrace(System.out); 565 failed = true; 566 } 567 568 if (failed) { 569 throw new RuntimeException("Test failed"); 570 } 571 } 572 } 573 574 final static class ServerCallable implements Callable<String> { 575 576 private final DTLSOverDatagram testCase; 577 private final DatagramSocket socket; 578 private final InetSocketAddress clientSocketAddr; 579 580 ServerCallable(DTLSOverDatagram testCase, DatagramSocket socket, 581 InetSocketAddress clientSocketAddr) { 582 583 this.testCase = testCase; 584 this.socket = socket; 585 this.clientSocketAddr = clientSocketAddr; 586 } 587 588 @Override 589 public String call() throws Exception { 590 try { 591 testCase.doServerSide(socket, clientSocketAddr); 592 } catch (Exception e) { 593 System.out.println("Exception in ServerCallable.call():"); 594 e.printStackTrace(System.out); 595 serverException = e; 596 597 if (testCase.isGoodJob()) { 598 throw e; 599 } else { 600 return "Well done, server!"; 601 } 602 } 603 604 if (testCase.isGoodJob()) { 605 return "Well done, server!"; 606 } else { 607 throw new Exception("No expected exception"); 608 } 609 } 610 } 611 612 final static class ClientCallable implements Callable<String> { 613 614 private final DTLSOverDatagram testCase; 615 private final DatagramSocket socket; 616 private final InetSocketAddress serverSocketAddr; 617 618 ClientCallable(DTLSOverDatagram testCase, DatagramSocket socket, 619 InetSocketAddress serverSocketAddr) { 620 621 this.testCase = testCase; 622 this.socket = socket; 623 this.serverSocketAddr = serverSocketAddr; 624 } 625 626 @Override 627 public String call() throws Exception { 628 try { 629 testCase.doClientSide(socket, serverSocketAddr); 630 } catch (Exception e) { 631 System.out.println("Exception in ClientCallable.call():"); 632 e.printStackTrace(System.out); 633 clientException = e; 634 635 if (testCase.isGoodJob()) { 636 throw e; 637 } else { 638 return "Well done, client!"; 639 } 640 } 641 642 if (testCase.isGoodJob()) { 643 return "Well done, client!"; 644 } else { 645 throw new Exception("No expected exception"); 646 } 647 } 648 } 649 650 final static void printHex(String prefix, ByteBuffer bb) { 651 HexDumpEncoder dump = new HexDumpEncoder(); 652 653 synchronized (System.out) { 654 System.out.println(prefix); 655 try { 656 dump.encodeBuffer(bb.slice(), System.out); 657 } catch (Exception e) { 658 // ignore 659 } 660 System.out.flush(); 661 } 662 } 663 664 final static void printHex(String prefix, 665 byte[] bytes, int offset, int length) { 666 667 HexDumpEncoder dump = new HexDumpEncoder(); 668 669 synchronized (System.out) { 670 System.out.println(prefix); 671 try { 672 ByteBuffer bb = ByteBuffer.wrap(bytes, offset, length); 673 dump.encodeBuffer(bb, System.out); 674 } catch (Exception e) { 675 // ignore 676 } 677 System.out.flush(); 678 } 679 } 680 681 static void log(String side, String message) { 682 System.out.println(side + ": " + message); 683 } 684 }