JDK-8213202 : Possible race condition in TLS 1.3 session resumption
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.net.ssl
  • Affected Version: 11,12
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2018-10-30
  • Updated: 2018-12-10
  • Resolved: 2018-11-21
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 12
12 b21Fixed
Related Reports
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
Making multiple concurrent TLS 1.3 connections sometimes throws exception "No PSK available. Unable to resume.". I've encountered this when using Maven with TLS 1.3 enable server. Maven is making multiple HTTPS requests and very often it fails.

I was able to reproduce this by making calls to Facebook, Cloudflare and personal servers running nginx and haproxy with OpenSSL 1.1.1. At least Facebook uses its own TLS 1.3 implementation so this seems like Java bug.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Please use attached program. Do note that this seems to be a race condition. As such the problem occurs randomly, sometimes it works, sometimes not. On Linux, adding 100ms of delay to all packet on test machine seems to help reproduce the problem (i.e. tc qdisc add dev eth0 root netem delay 100ms). Or just choose some TLS 1.3 server with high ping instead of www.cloudflare.com from test program.


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No Exception is thrown.
ACTUAL -
Following Exception occurs:

javax.net.ssl.SSLException: No PSK available. Unable to resume.
	at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:129)
	at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:308)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:255)
	at java.base/sun.security.ssl.ServerHello$T13ServerHelloConsumer.consume(ServerHello.java:1224)
	at java.base/sun.security.ssl.ServerHello$ServerHelloConsumer.onServerHello(ServerHello.java:984)
	at java.base/sun.security.ssl.ServerHello$ServerHelloConsumer.consume(ServerHello.java:872)
	at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
	at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
	at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421)
	at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:178)
	at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164)
	at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1152)
	at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1063)
	at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:402)
	at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
	at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
	at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:163)
	at btest.NetHttp.get(NetHttp.java:12)
	at btest.NetHttp.lambda$0(NetHttp.java:30)
	at java.base/java.lang.Thread.run(Thread.java:834)

---------- BEGIN SOURCE ----------
package btest;

import java.io.InputStreamReader;
import java.net.URL;

public class NetHttp {

    private static void get(String u) {
        try {
            var url = new URL(u);
            var conn = url.openConnection();
            conn.connect();
            try (var in = conn.getInputStream(); var in2 = new InputStreamReader(in)) {
                var buf = new char[100];
                while(true) {
                    int read = in2.read(buf);
                    if(read < 100) break;
                }
                System.out.println("ok");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        get("https://www.google.com"); // get the crypto stuff initialized, seems to increase changes of the problem appearing
        for (var i = 1; i <= 20; i++) {
            var t = new Thread(() -> {
                get("https://www.cloudflare.com");
            });
            t.start();
            Thread.sleep(50);
        }
    }

}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Disabling TLS 1.3 completely with -Dhttps.protocols=TLSv1,TLSv1.1,TLSv1.2

FREQUENCY : often



Comments
Since this bug seems to happen "often" with tls1.3, is this worthy of a backport to 11.0.2/3 ?
10-12-2018

> Will have a try with OpenSSL client and JSSE server. With my testing, I don't see this problem with JSSE server and s_client.
02-11-2018

I didn't meet this issue with JSSE server and client. Will have a try with OpenSSL client and JSSE server.
02-11-2018

Does this problem happen when using the JDK on the server side, or does it only show up with other servers?
01-11-2018

Reproduce this issue with OpenSSL (version 1.1.1) s_server. 1. Start s_server with the attached test.cer and test.key openssl s_server -cert test.cer -key test.key -WWW -accept 9443 2. Run JDK clients via attached HttpsConnectionTest.java.
01-11-2018

It looks no session should be resumed indeed.
01-11-2018

To reproduce the issue, run the attached test case. (I could reproduce on ubuntu linux after adding 100ms delay to each packet - tc qdisc add dev eth0 root netem delay 100ms) JDK 11 - Fail JDK 11.0.1 - Fail JDk 12-ea+ 13 - Fail javax.net.ssl.SSLException: No PSK available. Unable to resume. at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:129) at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:308) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:255) at java.base/sun.security.ssl.ServerHello$T13ServerHelloConsumer.consume(ServerHello.java:1224) at java.base/sun.security.ssl.ServerHello$ServerHelloConsumer.onServerHello(ServerHello.java:984) at java.base/sun.security.ssl.ServerHello$ServerHelloConsumer.consume(ServerHello.java:872) at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392) at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444) at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421) at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:178) at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164) at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1152) at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1063) at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:402) at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567) at java.base/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:857) at java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:689) at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1604) at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1509) at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:245) at JI9057881.get(JI9057881.java:10) at JI9057881.lambda$main$0(JI9057881.java:27) at java.base/java.lang.Thread.run(Thread.java:835)ok
31-10-2018