Add DTLS 1.3 SSLEngine example client and server applications

Co-Authored-By: chris@wolfssl.com <chris@wolfssl.com>
devin/1741390781-dtls13-examples
Devin AI 2025-03-07 23:44:32 +00:00
parent ad23ac20e9
commit 7189b086fe
4 changed files with 571 additions and 0 deletions

View File

@ -0,0 +1,271 @@
/* SSLEngineClientDTLS13.java
*
* Copyright (C) 2006-2025 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/**
* Example DTLS 1.3 client using SSLEngine.
*
* This client connects to a DTLS 1.3 server, sends data, receives a response,
* and then shuts down the connection. This example is compiled when
* 'ant examples' is run in the package root.
*
* $ ant examples
* $ ./examples/provider/SSLEngineClientDTLS13.sh
*
* For testing, start the SSLEngineServerDTLS13 example first:
*
* $ ./examples/provider/SSLEngineServerDTLS13.sh
*
*/
import java.util.*;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import javax.net.ssl.*;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import java.security.*;
import com.wolfssl.provider.jsse.WolfSSLProvider;
public class SSLEngineClientDTLS13 {
/* Keystore and connection settings */
private char[] psw;
private String clientKS = "./examples/provider/client.jks";
private String caKS = "./examples/provider/ca-server.jks";
private String jsseProv = "wolfJSSE";
private String host = "localhost";
private int port = 11119;
/* Initialize password from environment or system property */
{
String password = System.getProperty("javax.net.ssl.keyStorePassword");
if (password == null) {
password = System.getenv("WOLFSSL_PASSWORD");
}
if (password == null) {
System.err.println("Warning: Using empty password. Set password with " +
"javax.net.ssl.keyStorePassword property or WOLFSSL_PASSWORD env var");
password = "";
}
psw = password.toCharArray();
}
public SSLEngineClientDTLS13() {
try {
Security.addProvider(new WolfSSLProvider());
/* Set up KeyStore */
KeyStore clientKeyStore = KeyStore.getInstance("JKS");
clientKeyStore.load(new FileInputStream(clientKS), psw);
KeyManagerFactory km = KeyManagerFactory
.getInstance("SunX509", jsseProv);
km.init(clientKeyStore, psw);
/* Set up CA TrustManagerFactory */
KeyStore caKeyStore = KeyStore.getInstance("JKS");
caKeyStore.load(new FileInputStream(caKS), psw);
TrustManagerFactory tm = TrustManagerFactory
.getInstance("SunX509", jsseProv);
tm.init(caKeyStore);
/* Create SSLContext for DTLS 1.3 */
SSLContext ctx = SSLContext.getInstance("DTLSv1.3", jsseProv);
ctx.init(km.getKeyManagers(), tm.getTrustManagers(), null);
/* Create DatagramSocket for DTLS */
DatagramSocket clientSocket = new DatagramSocket();
InetAddress serverAddress = InetAddress.getByName(host);
/* Create SSLEngine */
SSLEngine engine = ctx.createSSLEngine(host, port);
engine.setUseClientMode(true);
/* Configure SSLEngine for DTLS */
SSLSession session = engine.getSession();
int appBufferSize = session.getApplicationBufferSize();
int netBufferSize = session.getPacketBufferSize();
/* Start handshake */
engine.beginHandshake();
/* Connect to server, perform handshake, send/receive data */
connectToServer(engine, clientSocket, serverAddress, port);
} catch(Exception e) {
e.printStackTrace();
}
}
private void connectToServer(SSLEngine engine, DatagramSocket socket,
InetAddress serverAddress, int serverPort) {
try {
SSLSession session = engine.getSession();
ByteBuffer appData = ByteBuffer.allocate(session.getApplicationBufferSize());
ByteBuffer netData = ByteBuffer.allocate(session.getPacketBufferSize());
ByteBuffer peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
ByteBuffer peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
HandshakeStatus status = engine.getHandshakeStatus();
boolean handshakeComplete = false;
/* Handle handshake */
while (!handshakeComplete) {
switch (status) {
case NEED_WRAP:
/* Wrap handshake data */
netData.clear();
SSLEngineResult wrapResult = engine.wrap(appData, netData);
status = wrapResult.getHandshakeStatus();
/* Send wrapped data to server */
netData.flip();
byte[] data = new byte[netData.remaining()];
netData.get(data);
DatagramPacket sendPacket = new DatagramPacket(
data, data.length, serverAddress, serverPort);
socket.send(sendPacket);
/* Handle tasks */
if (status == HandshakeStatus.NEED_TASK) {
Runnable task;
while ((task = engine.getDelegatedTask()) != null) {
task.run();
}
status = engine.getHandshakeStatus();
}
break;
case NEED_UNWRAP:
/* Receive data from server */
peerNetData.clear();
DatagramPacket packet = new DatagramPacket(
peerNetData.array(), peerNetData.capacity());
socket.receive(packet);
peerNetData.position(packet.getLength());
peerNetData.flip();
/* Unwrap received data */
SSLEngineResult result = engine.unwrap(peerNetData, peerAppData);
peerNetData.compact();
status = result.getHandshakeStatus();
/* Handle tasks */
if (status == HandshakeStatus.NEED_TASK) {
Runnable task;
while ((task = engine.getDelegatedTask()) != null) {
task.run();
}
status = engine.getHandshakeStatus();
}
break;
case FINISHED:
handshakeComplete = true;
break;
case NOT_HANDSHAKING:
handshakeComplete = true;
break;
default:
throw new IllegalStateException("Invalid handshake status: " + status);
}
}
/* Handshake complete, now send application data */
System.out.println("Handshake completed, sending data to server");
/* Prepare message to send */
String message = "Hello from DTLS 1.3 client!";
appData.clear();
appData.put(message.getBytes());
appData.flip();
/* Wrap the message */
netData.clear();
engine.wrap(appData, netData);
netData.flip();
/* Send message to server */
byte[] messageData = new byte[netData.remaining()];
netData.get(messageData);
DatagramPacket messagePacket = new DatagramPacket(
messageData, messageData.length, serverAddress, serverPort);
socket.send(messagePacket);
/* Receive response from server */
peerNetData.clear();
DatagramPacket responsePacket = new DatagramPacket(
peerNetData.array(), peerNetData.capacity());
socket.receive(responsePacket);
peerNetData.position(responsePacket.getLength());
peerNetData.flip();
/* Unwrap the response */
peerAppData.clear();
engine.unwrap(peerNetData, peerAppData);
peerAppData.flip();
/* Read the server's response */
byte[] serverMsg = new byte[peerAppData.remaining()];
peerAppData.get(serverMsg);
System.out.println("Received from server: " + new String(serverMsg));
/* Close the connection */
engine.closeOutbound();
/* Send close_notify */
netData.clear();
SSLEngineResult closeResult = engine.wrap(appData, netData);
netData.flip();
byte[] closeData = new byte[netData.remaining()];
netData.get(closeData);
DatagramPacket closePacket = new DatagramPacket(
closeData, closeData.length, serverAddress, serverPort);
socket.send(closePacket);
/* Wait for server's close_notify */
peerNetData.clear();
DatagramPacket closeResponsePacket = new DatagramPacket(
peerNetData.array(), peerNetData.capacity());
socket.setSoTimeout(5000); // Set timeout for close_notify
try {
socket.receive(closeResponsePacket);
peerNetData.position(closeResponsePacket.getLength());
peerNetData.flip();
engine.unwrap(peerNetData, peerAppData);
} catch (SocketTimeoutException e) {
/* Timeout waiting for server close_notify, continue */
}
System.out.println("Connection closed");
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new SSLEngineClientDTLS13();
}
}

View File

@ -0,0 +1,4 @@
#!/bin/bash
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib/:/usr/local/lib
java -classpath ./lib/wolfssl.jar:./lib/wolfssl-jsse.jar:./examples/build -Dsun.boot.library.path=./lib/ -Dwolfjsse.debug=true SSLEngineClientDTLS13 $@

View File

@ -0,0 +1,292 @@
/* SSLEngineServerDTLS13.java
*
* Copyright (C) 2006-2025 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/**
* Example DTLS 1.3 server using SSLEngine.
*
* This server waits in an infinite loop for client connections, and when
* connected creates a new thread for each connection. This example is compiled
* when 'ant examples' is run in the package root.
*
* $ ant examples
* $ ./examples/provider/SSLEngineServerDTLS13.sh
*
* For testing, connect with the SSLEngineClientDTLS13 example:
*
* $ ./examples/provider/SSLEngineClientDTLS13.sh
*
*/
import java.util.*;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import javax.net.ssl.*;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import java.security.*;
import com.wolfssl.provider.jsse.WolfSSLProvider;
public class SSLEngineServerDTLS13 {
/* Keystore and connection settings */
private char[] psw;
private String serverKS = "./examples/provider/server.jks";
private String serverTS = "./examples/provider/ca-client.jks";
private String jsseProv = "wolfJSSE";
private int serverPort = 11119;
/* Initialize password from environment or system property */
{
String password = System.getProperty("javax.net.ssl.keyStorePassword");
if (password == null) {
password = System.getenv("WOLFSSL_PASSWORD");
}
if (password == null) {
System.err.println("Warning: Using empty password. Set password with " +
"javax.net.ssl.keyStorePassword property or WOLFSSL_PASSWORD env var");
password = "";
}
psw = password.toCharArray();
}
public SSLEngineServerDTLS13() {
try {
Security.addProvider(new WolfSSLProvider());
/* Set up KeyStore */
KeyStore serverKeyStore = KeyStore.getInstance("JKS");
serverKeyStore.load(new FileInputStream(serverKS), psw);
KeyManagerFactory km = KeyManagerFactory
.getInstance("SunX509", jsseProv);
km.init(serverKeyStore, psw);
/* Set up CA TrustManagerFactory */
KeyStore caKeyStore = KeyStore.getInstance("JKS");
caKeyStore.load(new FileInputStream(serverTS), psw);
TrustManagerFactory tm = TrustManagerFactory
.getInstance("SunX509", jsseProv);
tm.init(caKeyStore);
/* Create SSLContext for DTLS 1.3 */
SSLContext ctx = SSLContext.getInstance("DTLSv1.3", jsseProv);
ctx.init(km.getKeyManagers(), tm.getTrustManagers(), null);
/* Create DatagramSocket for DTLS */
DatagramSocket serverSocket = new DatagramSocket(serverPort);
System.out.println("DTLS 1.3 Server listening on port " + serverPort);
while (true) {
/* Create buffer for client's initial message */
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
/* Wait for client connection */
serverSocket.receive(packet);
/* Create and start new client handler thread */
ClientHandler client = new ClientHandler(ctx, serverSocket, packet);
client.start();
}
} catch(Exception e) {
e.printStackTrace();
}
}
class ClientHandler extends Thread {
private SSLEngine engine;
private DatagramSocket socket;
private DatagramPacket clientPacket;
private InetAddress clientAddress;
private int clientPort;
public ClientHandler(SSLContext ctx, DatagramSocket s,
DatagramPacket p) {
socket = s;
clientPacket = p;
clientAddress = p.getAddress();
clientPort = p.getPort();
/* Create SSLEngine for this client */
engine = ctx.createSSLEngine(clientAddress.getHostAddress(),
clientPort);
engine.setUseClientMode(false);
engine.setNeedClientAuth(true);
}
public void run() {
try {
/* Configure SSLEngine for DTLS */
SSLSession session = engine.getSession();
int appBufferSize = session.getApplicationBufferSize();
int netBufferSize = session.getPacketBufferSize();
/* Start handshake */
engine.beginHandshake();
/* Process handshake and data exchange */
handleConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
private void handleConnection() {
try {
SSLSession session = engine.getSession();
ByteBuffer appData = ByteBuffer.allocate(session.getApplicationBufferSize());
ByteBuffer netData = ByteBuffer.allocate(session.getPacketBufferSize());
ByteBuffer peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
ByteBuffer peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
HandshakeStatus status = engine.getHandshakeStatus();
boolean handshakeComplete = false;
/* Handle handshake */
while (!handshakeComplete) {
switch (status) {
case NEED_UNWRAP:
/* Receive data from client */
peerNetData.clear();
DatagramPacket packet = new DatagramPacket(
peerNetData.array(), peerNetData.capacity());
socket.receive(packet);
peerNetData.position(packet.getLength());
peerNetData.flip();
/* Unwrap received data */
SSLEngineResult result = engine.unwrap(peerNetData, peerAppData);
peerNetData.compact();
status = result.getHandshakeStatus();
/* Handle tasks */
if (status == HandshakeStatus.NEED_TASK) {
Runnable task;
while ((task = engine.getDelegatedTask()) != null) {
task.run();
}
status = engine.getHandshakeStatus();
}
break;
case NEED_WRAP:
/* Wrap handshake data */
netData.clear();
SSLEngineResult wrapResult = engine.wrap(appData, netData);
status = wrapResult.getHandshakeStatus();
/* Send wrapped data to client */
netData.flip();
byte[] data = new byte[netData.remaining()];
netData.get(data);
DatagramPacket sendPacket = new DatagramPacket(
data, data.length, clientAddress, clientPort);
socket.send(sendPacket);
/* Handle tasks */
if (status == HandshakeStatus.NEED_TASK) {
Runnable task;
while ((task = engine.getDelegatedTask()) != null) {
task.run();
}
status = engine.getHandshakeStatus();
}
break;
case FINISHED:
handshakeComplete = true;
break;
case NOT_HANDSHAKING:
handshakeComplete = true;
break;
default:
throw new IllegalStateException("Invalid handshake status: " + status);
}
}
/* Handshake complete, now exchange application data */
System.out.println("Handshake completed, waiting for data from client");
/* Receive data from client */
peerNetData.clear();
DatagramPacket dataPacket = new DatagramPacket(
peerNetData.array(), peerNetData.capacity());
socket.receive(dataPacket);
peerNetData.position(dataPacket.getLength());
peerNetData.flip();
/* Unwrap received data */
peerAppData.clear();
SSLEngineResult dataResult = engine.unwrap(peerNetData, peerAppData);
peerAppData.flip();
/* Read the client message */
byte[] clientMsg = new byte[peerAppData.remaining()];
peerAppData.get(clientMsg);
String message = new String(clientMsg);
System.out.println("Received from client: " + message);
/* Echo the message back to client */
appData.clear();
appData.put(clientMsg);
appData.flip();
/* Wrap the response */
netData.clear();
engine.wrap(appData, netData);
netData.flip();
/* Send response to client */
byte[] responseData = new byte[netData.remaining()];
netData.get(responseData);
DatagramPacket responsePacket = new DatagramPacket(
responseData, responseData.length, clientAddress, clientPort);
socket.send(responsePacket);
/* Close the connection */
engine.closeOutbound();
/* Send close_notify */
netData.clear();
SSLEngineResult closeResult = engine.wrap(appData, netData);
netData.flip();
byte[] closeData = new byte[netData.remaining()];
netData.get(closeData);
DatagramPacket closePacket = new DatagramPacket(
closeData, closeData.length, clientAddress, clientPort);
socket.send(closePacket);
System.out.println("Connection closed");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new SSLEngineServerDTLS13();
}
}

View File

@ -0,0 +1,4 @@
#!/bin/bash
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib/:/usr/local/lib
java -classpath ./lib/wolfssl.jar:./lib/wolfssl-jsse.jar:./examples/build -Dsun.boot.library.path=./lib/ -Dwolfjsse.debug=true SSLEngineServerDTLS13 $@