wolfssljni/examples/provider/SSLEngineServerDTLS13.java

293 lines
12 KiB
Java

/* 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();
}
}