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

Pre shared key sent under both session_ticket and pre_shared_key extensions

    XMLWordPrintable

Details

    • b26
    • generic
    • generic
    • Verified

    Description

      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


      Attachments

        Issue Links

          Activity

            People

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

              Dates

                Created:
                Updated:
                Resolved: