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

Reply via email to