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;
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) {
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,
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);
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;
|
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 private static int MAX_HANDSHAKE_LOOPS = 200;
52 private static int MAX_APP_READ_LOOPS = 60;
53 private static int SOCKET_TIMEOUT = 10 * 1000; // in millis
54 private static int BUFFER_SIZE = 1024;
55 private static int MAXIMUM_PACKET_SIZE = 1024;
56
57 /*
58 * The following is to set up the keystores.
59 */
60 private static String pathToStores = "../etc";
61 private static String keyStoreFile = "keystore";
62 private static String trustStoreFile = "truststore";
63 private static String passwd = "passphrase";
64
65 private static String keyFilename =
66 System.getProperty("test.src", ".") + "/" + pathToStores +
67 "/" + keyStoreFile;
68 private static String trustFilename =
69 System.getProperty("test.src", ".") + "/" + pathToStores +
70 "/" + trustStoreFile;
139
140 return engine;
141 }
142
143 // handshake
144 void handshake(SSLEngine engine, DatagramSocket socket,
145 SocketAddress peerAddr, String side) throws Exception {
146
147 boolean endLoops = false;
148 int loops = MAX_HANDSHAKE_LOOPS;
149 engine.beginHandshake();
150 while (!endLoops &&
151 (serverException == null) && (clientException == null)) {
152
153 if (--loops < 0) {
154 throw new RuntimeException(
155 "Too much loops to produce handshake packets");
156 }
157
158 SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
159 log(side, "=======handshake(" + loops + ", " + hs + ")=======");
160 if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
161 hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
162
163 log(side, "Receive DTLS records, handshake status is " + hs);
164
165 ByteBuffer iNet;
166 ByteBuffer iApp;
167 if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
168 byte[] buf = new byte[BUFFER_SIZE];
169 DatagramPacket packet = new DatagramPacket(buf, buf.length);
170 try {
171 socket.receive(packet);
172 } catch (SocketTimeoutException ste) {
173 log(side, "Warning: " + ste);
174
175 List<DatagramPacket> packets = new ArrayList<>();
176 boolean finished = onReceiveTimeout(
177 engine, peerAddr, side, packets);
178
179 for (DatagramPacket p : packets) {
219 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
220 throw new Exception("Buffer underflow: " +
221 "incorrect client maximum fragment size");
222 } // otherwise, ignore this packet
223 } else if (rs == SSLEngineResult.Status.CLOSED) {
224 throw new Exception(
225 "SSL engine closed, handshake status is " + hs);
226 } else {
227 throw new Exception("Can't reach here, result is " + rs);
228 }
229
230 if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
231 log(side, "Handshake status is FINISHED, finish the loop");
232 endLoops = true;
233 }
234 } else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
235 List<DatagramPacket> packets = new ArrayList<>();
236 boolean finished = produceHandshakePackets(
237 engine, peerAddr, side, packets);
238
239 log(side, "Produced " + packets.size() + " packets");
240 for (DatagramPacket p : packets) {
241 socket.send(p);
242 }
243
244 if (finished) {
245 log(side, "Handshake status is FINISHED "
246 + "after producing handshake packets, "
247 + "finish the loop");
248 endLoops = true;
249 }
250 } else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
251 runDelegatedTasks(engine);
252 } else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
253 log(side,
254 "Handshake status is NOT_HANDSHAKING, finish the loop");
255 endLoops = true;
256 } else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
257 throw new Exception(
258 "Unexpected status, SSLEngine.getHandshakeStatus() "
259 + "shouldn't return FINISHED");
260 } else {
261 throw new Exception(
262 "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 //
283 // According to the spec, SSLEngine.getHandshakeStatus() can't
284 // return FINISHED.
285 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
286 throw new Exception("Unexpected handshake status " + hs);
287 }
288 }
289
290 // deliver application data
291 void deliverAppData(SSLEngine engine, DatagramSocket socket,
292 ByteBuffer appData, SocketAddress peerAddr) throws Exception {
293
294 // Note: have not consider the packet loses
295 List<DatagramPacket> packets =
296 produceApplicationPackets(engine, appData, peerAddr);
297 appData.flip();
298 for (DatagramPacket p : packets) {
299 socket.send(p);
300 }
301 }
302
303 // receive application data
304 void receiveAppData(SSLEngine engine,
333 boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr,
334 String side, List<DatagramPacket> packets) throws Exception {
335
336 boolean endLoops = false;
337 int loops = MAX_HANDSHAKE_LOOPS;
338 while (!endLoops &&
339 (serverException == null) && (clientException == null)) {
340
341 if (--loops < 0) {
342 throw new RuntimeException(
343 "Too much loops to produce handshake packets");
344 }
345
346 ByteBuffer oNet = ByteBuffer.allocate(32768);
347 ByteBuffer oApp = ByteBuffer.allocate(0);
348 SSLEngineResult r = engine.wrap(oApp, oNet);
349 oNet.flip();
350
351 SSLEngineResult.Status rs = r.getStatus();
352 SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus();
353 log(side, "====packet(" + loops + ", " + rs + ", " + hs + ")====");
354 if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
355 // the client maximum fragment size config does not work?
356 throw new Exception("Buffer overflow: " +
357 "incorrect server maximum fragment size");
358 } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
359 log(side,
360 "Produce handshake packets: BUFFER_UNDERFLOW occured");
361 log(side,
362 "Produce handshake packets: Handshake status: " + hs);
363 // bad packet, or the client maximum fragment size
364 // config does not work?
365 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
366 throw new Exception("Buffer underflow: " +
367 "incorrect server maximum fragment size");
368 } // otherwise, ignore this packet
369 } else if (rs == SSLEngineResult.Status.CLOSED) {
370 throw new Exception("SSLEngine has closed");
371 } else if (rs == SSLEngineResult.Status.OK) {
372 // OK
373 } else {
374 throw new Exception("Can't reach here, result is " + rs);
375 }
376
377 // SSLEngineResult.Status.OK:
378 if (oNet.hasRemaining()) {
379 byte[] ba = new byte[oNet.remaining()];
380 oNet.get(ba);
381 DatagramPacket packet = createHandshakePacket(ba, socketAddr);
382 packets.add(packet);
441 } else if (rs == SSLEngineResult.Status.CLOSED) {
442 throw new Exception("SSLEngine has closed");
443 } else if (rs == SSLEngineResult.Status.OK) {
444 // OK
445 } else {
446 throw new Exception("Can't reach here, result is " + rs);
447 }
448
449 // SSLEngineResult.Status.OK:
450 if (appNet.hasRemaining()) {
451 byte[] ba = new byte[appNet.remaining()];
452 appNet.get(ba);
453 DatagramPacket packet =
454 new DatagramPacket(ba, ba.length, socketAddr);
455 packets.add(packet);
456 }
457
458 return packets;
459 }
460
461 // Get a datagram packet for the specified handshake type.
462 static DatagramPacket getPacket(
463 List<DatagramPacket> packets, byte handshakeType) {
464 boolean matched = false;
465 for (DatagramPacket packet : packets) {
466 byte[] data = packet.getData();
467 int offset = packet.getOffset();
468 int length = packet.getLength();
469
470 // Normally, this pakcet should be a handshake message
471 // record. However, even if the underlying platform
472 // splits the record more, we don't really worry about
473 // the improper packet loss because DTLS implementation
474 // should be able to handle packet loss properly.
475 //
476 // See RFC 6347 for the detailed format of DTLS records.
477 if (handshakeType == -1) { // ChangeCipherSpec
478 // Is it a ChangeCipherSpec message?
479 matched = (length == 14) && (data[offset] == 0x14);
480 } else if ((length >= 25) && // 25: handshake mini size
481 (data[offset] == 0x16)) { // a handshake message
482
483 // check epoch number for initial handshake only
484 if (data[offset + 3] == 0x00) { // 3,4: epoch
485 if (data[offset + 4] == 0x00) { // plaintext
486 matched =
487 (data[offset + 13] == handshakeType);
488 } else { // cipherext
489 // The 1st ciphertext is a Finished message.
490 //
491 // If it is not proposed to loss the Finished
492 // message, it is not necessary to check the
493 // following packets any mroe as a Finished
494 // message is the last handshake message.
495 matched = (handshakeType == 20);
496 }
497 }
498 }
499
500 if (matched) {
501 return packet;
502 }
503 }
504
505 return null;
506 }
507
508 // run delegated tasks
509 void runDelegatedTasks(SSLEngine engine) throws Exception {
510 Runnable runnable;
511 while ((runnable = engine.getDelegatedTask()) != null) {
512 runnable.run();
513 }
514
515 SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
516 if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
517 throw new Exception("handshake shouldn't need additional tasks");
518 }
519 }
520
521 // retransmission if timeout
522 boolean onReceiveTimeout(SSLEngine engine, SocketAddress socketAddr,
523 String side, List<DatagramPacket> packets) throws Exception {
524
525 SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
526 if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
527 return false;
|