Skip to content

Commit abf7f34

Browse files
authored
Merge pull request #340 from cconlon/jssePsk
Add JSSE-level PSK support via WolfSSLParameters
2 parents db577ac + b22331f commit abf7f34

16 files changed

Lines changed: 2070 additions & 198 deletions
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
/* PskClientEngine.java
2+
*
3+
* Copyright (C) 2006-2026 wolfSSL Inc.
4+
*
5+
* This file is part of wolfSSL.
6+
*
7+
* wolfSSL is free software; you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation; either version 2 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* wolfSSL is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
20+
*/
21+
22+
import java.io.IOException;
23+
import java.net.InetSocketAddress;
24+
import java.nio.ByteBuffer;
25+
import java.nio.channels.SocketChannel;
26+
import java.security.Security;
27+
import javax.net.ssl.SSLContext;
28+
import javax.net.ssl.SSLEngine;
29+
import javax.net.ssl.SSLEngineResult;
30+
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
31+
import javax.net.ssl.SSLSession;
32+
33+
import com.wolfssl.WolfSSLSession;
34+
import com.wolfssl.WolfSSLPskClientCallback;
35+
import com.wolfssl.provider.jsse.WolfSSLProvider;
36+
import com.wolfssl.provider.jsse.WolfSSLParameters;
37+
38+
/**
39+
* Simple PSK client example using SSLEngine and WolfSSLParameters.
40+
*
41+
* This example demonstrates configuring PSK through WolfSSLParameters with
42+
* SSLEngine.
43+
*
44+
* Usage: PskClientEngine [host] [port]
45+
*/
46+
public class PskClientEngine {
47+
48+
private static final String DEFAULT_HOST = "localhost";
49+
private static final int DEFAULT_PORT = 11111;
50+
51+
public static void main(String[] args) throws Exception {
52+
53+
String host = DEFAULT_HOST;
54+
int port = DEFAULT_PORT;
55+
56+
if (args.length >= 1) {
57+
host = args[0];
58+
}
59+
if (args.length >= 2) {
60+
port = Integer.parseInt(args[1]);
61+
}
62+
63+
/* Install wolfJSSE provider */
64+
Security.insertProviderAt(new WolfSSLProvider(), 1);
65+
66+
SSLContext ctx = SSLContext.getInstance("TLSv1.2", "wolfJSSE");
67+
ctx.init(null, null, null);
68+
69+
/* Create SSLEngine */
70+
SSLEngine engine = ctx.createSSLEngine(host, port);
71+
engine.setUseClientMode(true);
72+
73+
/* Find a PSK cipher suite available in this build */
74+
String pskCipher = findPskCipher(engine.getSupportedCipherSuites());
75+
76+
/* Configure PSK via WolfSSLParameters */
77+
WolfSSLParameters params = new WolfSSLParameters();
78+
params.setPskClientCb(new MyPskClientCallback());
79+
params.setCipherSuites(new String[]{pskCipher});
80+
engine.setSSLParameters(params);
81+
System.out.println("Using cipher: " + pskCipher);
82+
83+
/* Connect via SocketChannel */
84+
SocketChannel sc = SocketChannel.open(
85+
new InetSocketAddress(host, port));
86+
System.out.println("Connected to " + host + ":" + port);
87+
88+
try {
89+
/* Perform handshake */
90+
doHandshake(engine, sc);
91+
System.out.println("SSL handshake complete");
92+
SSLSession sess = engine.getSession();
93+
System.out.println(" Protocol: " + sess.getProtocol());
94+
System.out.println(" Cipher: " + sess.getCipherSuite());
95+
96+
/* Send application data */
97+
String msg = "Hello from PSK Engine client!";
98+
ByteBuffer appOut = ByteBuffer.wrap(msg.getBytes());
99+
ByteBuffer netOut = ByteBuffer.allocate(
100+
sess.getPacketBufferSize());
101+
102+
SSLEngineResult res = engine.wrap(appOut, netOut);
103+
netOut.flip();
104+
while (netOut.hasRemaining()) {
105+
sc.write(netOut);
106+
}
107+
System.out.println("Sent: " + msg);
108+
109+
/* Receive response */
110+
ByteBuffer netIn = ByteBuffer.allocate(
111+
sess.getPacketBufferSize());
112+
ByteBuffer appIn = ByteBuffer.allocate(
113+
sess.getApplicationBufferSize());
114+
115+
sc.read(netIn);
116+
netIn.flip();
117+
res = engine.unwrap(netIn, appIn);
118+
appIn.flip();
119+
byte[] data = new byte[appIn.remaining()];
120+
appIn.get(data);
121+
System.out.println("Received: " + new String(data));
122+
123+
engine.closeOutbound();
124+
125+
} finally {
126+
sc.close();
127+
}
128+
129+
System.out.println("Connection closed");
130+
}
131+
132+
/**
133+
* Perform TLS handshake using SSLEngine over SocketChannel.
134+
*/
135+
private static void doHandshake(SSLEngine engine, SocketChannel sc)
136+
throws Exception {
137+
138+
SSLSession sess = engine.getSession();
139+
int netSize = sess.getPacketBufferSize();
140+
int appSize = sess.getApplicationBufferSize();
141+
142+
ByteBuffer localNet = ByteBuffer.allocate(netSize);
143+
ByteBuffer peerNet = ByteBuffer.allocate(netSize);
144+
ByteBuffer localApp = ByteBuffer.allocate(0);
145+
ByteBuffer peerApp = ByteBuffer.allocate(appSize);
146+
147+
engine.beginHandshake();
148+
HandshakeStatus hs = engine.getHandshakeStatus();
149+
150+
while (hs != HandshakeStatus.FINISHED &&
151+
hs != HandshakeStatus.NOT_HANDSHAKING) {
152+
153+
SSLEngineResult res;
154+
switch (hs) {
155+
case NEED_WRAP:
156+
localNet.clear();
157+
res = engine.wrap(localApp, localNet);
158+
hs = res.getHandshakeStatus();
159+
localNet.flip();
160+
while (localNet.hasRemaining()) {
161+
sc.write(localNet);
162+
}
163+
break;
164+
165+
case NEED_UNWRAP:
166+
if (sc.read(peerNet) < 0) {
167+
throw new IOException(
168+
"Channel closed during handshake");
169+
}
170+
peerNet.flip();
171+
res = engine.unwrap(peerNet, peerApp);
172+
peerNet.compact();
173+
hs = res.getHandshakeStatus();
174+
175+
if (res.getStatus() ==
176+
SSLEngineResult.Status.BUFFER_UNDERFLOW) {
177+
/* Need more data, continue reading */
178+
continue;
179+
}
180+
break;
181+
182+
case NEED_TASK:
183+
Runnable task;
184+
while ((task = engine.getDelegatedTask()) != null) {
185+
task.run();
186+
}
187+
hs = engine.getHandshakeStatus();
188+
break;
189+
190+
default:
191+
break;
192+
}
193+
}
194+
}
195+
196+
/**
197+
* Find first available ephemeral PSK cipher suite from supported list.
198+
* Prefers ECDHE over DHE, AES-GCM over others. Falls back to static PSK
199+
* if no ephemeral suite is available.
200+
*/
201+
private static String findPskCipher(String[] suites) {
202+
203+
String ecdhe = null;
204+
String dhe = null;
205+
String plain = null;
206+
207+
for (String s : suites) {
208+
if (s.startsWith("TLS_ECDHE_PSK_WITH_")) {
209+
if (ecdhe == null || s.contains("GCM")) {
210+
ecdhe = s;
211+
}
212+
}
213+
else if (s.startsWith("TLS_DHE_PSK_WITH_")) {
214+
if (dhe == null || s.contains("GCM")) {
215+
dhe = s;
216+
}
217+
}
218+
else if (s.startsWith("TLS_PSK_WITH_")) {
219+
if (plain == null) {
220+
plain = s;
221+
}
222+
}
223+
}
224+
225+
if (ecdhe != null) { return ecdhe; }
226+
if (dhe != null) { return dhe; }
227+
if (plain != null) { return plain; }
228+
229+
throw new RuntimeException(
230+
"No PSK cipher suites available. " +
231+
"No PSK cipher suites compiled into wolfSSL");
232+
}
233+
234+
/**
235+
* PSK client callback implementation.
236+
*/
237+
static class MyPskClientCallback implements WolfSSLPskClientCallback {
238+
239+
public long pskClientCallback(WolfSSLSession ssl, String hint,
240+
StringBuffer identity, long idMaxLen, byte[] key, long keyMaxLen) {
241+
242+
System.out.println("PSK Client Callback:");
243+
System.out.println(" Hint: " + hint);
244+
245+
String id = "Client_identity";
246+
if (id.length() > idMaxLen || keyMaxLen < 4) {
247+
return 0;
248+
}
249+
identity.append(id);
250+
251+
key[0] = 26;
252+
key[1] = 43;
253+
key[2] = 60;
254+
key[3] = 77;
255+
256+
return 4;
257+
}
258+
}
259+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
3+
cd ./examples/build
4+
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../lib/:/usr/local/lib
5+
java -classpath ../../lib/wolfssl.jar:../../lib/wolfssl-jsse.jar:./ -Dsun.boot.library.path=../../lib/ PskClientEngine "$@"

0 commit comments

Comments
 (0)