Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8277307

Pre shared key sent under both session_ticket and pre_shared_key extensions

XMLWordPrintable

    • b26
    • generic
    • generic
    • Verified

        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


              djelinski Daniel Jelinski
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              15 Start watching this issue

                Created:
                Updated:
                Resolved: