Hi, The code below reproduces a bug where session resumption is tested. Turns out that session resumption does happen on the client and on the server, but on the client the session id is not replaced with the resumed session id (while it is on the server).
The code prints: session1 = Session(1535051727187|TLS_AES_128_GCM_SHA256) - [29, -38, ...] session2 = Session(1535051727187|TLS_AES_128_GCM_SHA256) - [11, -20, ...] where it can be seen that the second session has the same creationTime as the first, but different session id (the array). About the code below, note how it is necessary to attempt a read after the first handshake to read the NewSessionTicket post-handshake message that is otherwise not consumed by the client. This was not necessary before. Scenarios where clients don't read from the server (or where many clients are created before one starts reading) will have a harder time to use session resumption. Thanks! ---- public static void main(String[] args) throws Exception { String host = "localhost"; int port = 8443; SSLContext serverSSLContext = ...; SSLServerSocket sslServer = (SSLServerSocket)serverSSLContext.getServerSocketFactory().createServerSocket(port); Runnable serverReader = () -> { try { SSLSocket sslSocket = (SSLSocket)sslServer.accept(); InputStream input = sslSocket.getInputStream(); while (true) { int read = input.read(); if (read < 0) break; } } catch (Throwable x) { x.printStackTrace(); } }; new Thread(serverReader).start(); SSLContext clientSSLContext = ...; Socket client1 = new Socket(host, port); SSLSocket sslClient1 = (SSLSocket)clientSSLContext.getSocketFactory().createSocket(client1, host, port, true); AtomicReference<String> session1 = new AtomicReference<>(); CountDownLatch handshakeLatch1 = new CountDownLatch(1); sslClient1.addHandshakeCompletedListener(event -> { SSLSession session = event.getSession(); session1.set(session.toString() + " - " + Arrays.toString(session.getId())); handshakeLatch1.countDown(); }); sslClient1.startHandshake(); handshakeLatch1.await(1, TimeUnit.SECONDS); try { sslClient1.setSoTimeout(1000); sslClient1.getInputStream().read(); } catch (SocketTimeoutException expected) { } sslClient1.close(); new Thread(serverReader).start(); Socket client2 = new Socket(host, port); SSLSocket sslClient2 = (SSLSocket)clientSSLContext.getSocketFactory().createSocket(client2, host, port, true); AtomicReference<String> session2 = new AtomicReference<>(); CountDownLatch handshakeLatch2 = new CountDownLatch(1); sslClient2.addHandshakeCompletedListener(event -> { SSLSession session = event.getSession(); session2.set(session.toString() + " - " + Arrays.toString(session.getId())); handshakeLatch2.countDown(); }); sslClient2.startHandshake(); handshakeLatch2.await(1, TimeUnit.SECONDS); System.err.println("session1 = " + session1); System.err.println("session2 = " + session2); sslClient2.close(); } -- Simone Bordet --- Finally, no matter how good the architecture and design are, to deliver bug-free software with optimal performance and reliability, the implementation technique must be flawless. Victoria Livschitz