ADDITIONAL SYSTEM INFORMATION :
Tested on orcacle JDK 17.0.1 and Open JDK 17+35
A DESCRIPTION OF THE PROBLEM :
This occurs when communicating jvm to jvm on java 17 with TLSv1.3. The pre_shared_key is sent under the session_ticket extension as well.
REGRESSION : Last worked in version 11.0.13
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the example code, provide keystore and truststore as instructed in the code example
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
On the second request, the pre_shared_key extension populated in client hello, but not in session_ticket extension
ACTUAL -
Both extensions carry the same data.
---------- BEGIN SOURCE ----------
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
/*
* This code is based on https://blog.gypsyengineer.com/en/security/an-example-of-tls-13-client-and-server-on-java.html
* to reproduce a TLS issue with pre_shared_key and session_ticket.
* Don't forget to set the following system properties when you run the class:
*
* javax.net.ssl.keyStore
* javax.net.ssl.keyStorePassword
* javax.net.ssl.trustStore
* javax.net.ssl.trustStorePassword
*
* More details can be found in JSSE docs.
*
* For example:
*
* java -cp classes \
* -Djavax.net.ssl.keyStore=keystore \
* -Djavax.net.ssl.keyStorePassword=passphrase \
* -Djavax.net.ssl.trustStore=keystore \
* -Djavax.net.ssl.trustStorePassword=passphrase \
* TLSv13Test
*
* For testing purposes, you can download the keystore file from
*
* https://github.com/openjdk/jdk/tree/master/test/jdk/javax/net/ssl/etc
*/
public class TLSv13Test {
private static final int delay = 1000; // in millis
private static final String message =
"Like most of life's problems, this one can be solved with bending!";
public static void main(String[] args) throws Exception {
try (EchoServer server = EchoServer.create()) {
new Thread(server).start();
Thread.sleep(delay);
var port = server.port();
try (SSLSocket socket = createSocket("localhost", port)) {
talkToServer(socket);
}
Thread.sleep(delay);
try (SSLSocket socket = createSocket("localhost", port)) {
// This time on JDK17 (OpenJDK Runtime Environment Temurin-17+35 (build 17+35))
// the Client Hello will contain the same data in pre_shared_key and session_ticket
// as can be observed in Wireshark for example
talkToServer(socket);
}
}
}
private static void talkToServer(SSLSocket socket) throws IOException {
InputStream is = new BufferedInputStream(socket.getInputStream());
OutputStream os = new BufferedOutputStream(socket.getOutputStream());
os.write(message.getBytes());
os.flush();
byte[] data = new byte[2048];
int len = is.read(data);
if (len <= 0) {
throw new IOException("no data received");
}
System.out.printf("client received %d bytes: %s%n",
len, new String(data, 0, len));
}
public static SSLSocket createSocket(String host, int port) throws IOException {
SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault()
.createSocket(host, port);
socket.setEnableSessionCreation(true);
return socket;
}
public static class EchoServer implements Runnable, AutoCloseable {
private static final int FREE_PORT = 0;
private final SSLServerSocket sslServerSocket;
private EchoServer(SSLServerSocket sslServerSocket) {
this.sslServerSocket = sslServerSocket;
}
public int port() {
return sslServerSocket.getLocalPort();
}
@Override
public void close() throws IOException {
if (sslServerSocket != null && !sslServerSocket.isClosed()) {
sslServerSocket.close();
}
}
@Override
public void run() {
System.out.printf("server started on port %d%n", port());
while (!sslServerSocket.isClosed()) {
try (SSLSocket socket = (SSLSocket) sslServerSocket.accept()) {
System.out.println("accepted");
InputStream is = new BufferedInputStream(socket.getInputStream());
OutputStream os = new BufferedOutputStream(socket.getOutputStream());
byte[] data = new byte[2048];
int len = is.read(data);
if (len <= 0) {
throw new IOException("no data received");
}
System.out.printf("server received %d bytes: %s%n",
len, new String(data, 0, len));
os.write(data, 0, len);
os.flush();
} catch (Exception e) {
System.out.printf("exception: %s%n", e.getMessage());
e.printStackTrace(System.err);
}
}
}
public static EchoServer create() throws IOException {
return create(FREE_PORT);
}
public static EchoServer create(int port) throws IOException {
SSLServerSocket socket = (SSLServerSocket)
SSLServerSocketFactory.getDefault().createServerSocket(port);
return new EchoServer(socket);
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Set jdk.tls.client.enableSessionTicketExtension=false on the client
FREQUENCY : often