JDK-8211806 : TLS 1.3 handshake server name indication is missing on a session resume
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.net.ssl
  • Affected Version: 11
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2018-10-06
  • Updated: 2020-11-23
  • Resolved: 2018-10-20
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 11 JDK 8 Other
11.0.2Fixed 8u261Fixed openjdk8u272Fixed
Related Reports
Duplicate :  
Description
A customer showed me a case where a TLSv1.3 handshake is failing, but passes on TLSv1.2 on JDK 11.  It also doesn't fail in TLSv1.2 on JDK 10. 

Customer is primarily using:  URL.openConnection()/getInputStream() 

In a simple connection, I am seeing the right server_name extension for <hostname> going out.  The certificates received from the server are also for <hostname>:

  "extensions"          : [
    "server_name (0)": {
      type=host_name (0), value=<hostname>
    },
...deleted...
javax.net.ssl|DEBUG|01|main|2018-10-05 14:41:28.941 PDT|CertificateMessage.java:1148|Consuming server Certificate handshake message (
"Certificate": {
  "certificate_request_context": "",
  "certificate_list": [
  {
    "certificate" : {
      "issuer"             : "CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US",
      "subject"            : "CN=<hostname>",

with alternate names for:

          SubjectAlternativeName [
            DNSName: <hostname>
            DNSName: www.<hostname>
          ]

However, in the debug log he showed me, I didn't see the server_name extension going out in the second connection, and noticed this in the debug log:

javax.net.ssl|DEBUG|81|Thread-78|2018-09-27 18:20:46.303 PDT|Utilities.java:73|the previous server name in SNI (type=host_name (0), value=<hostname>) was replaced with (type=host_name (0), value=<hostname>)

and the certificate received was for:

      "subject"            : "CN=*.<hostname2>,

Are they running <hostname> on a virtual server named <hostname2>?

So something about the server name indication seems to be going haywire.

That's my best guess. 

----some additional notes----

It appears to be a race condition between how connections are created, as it doesn't always happen.  Sometimes the app will work fine, but othertimes it's almost immediate.  Outputting "all" debug information reduced the frequency of occurrence, but would eventually happen.

There is currently not a simple reproducible test case.  But the two error logs show the same error, one from the debug output, and one from the wireshark output.
 
See the later comments for additional information.

Comments
Fix Request - Justification: This issue causes clients performing TLSv1.3 session resumption to not properly assert the server_name extension in the resumption ClientHello handshake message. This can cause problems when used with servers that require the server_name extension as an indication of which virtually-hosted server to connect the client to. It is strongly recommended in RFC 8446 that clients provide this extension when possible. ���- Risk Analysis: ���The fix for this is a one-line addition to the PostHandshakeContext constructor that preserves the server name from the initial handshake. It only affects TLSv1.3 handshakes. Low risk. - Patch Application: This patch applies without error to jdk11u. - Webrev: (jdk/jdk) http://cr.openjdk.java.net/~jnimeh/reviews/8211806/webrev.02/ - Testing: A test has been provided with the bugfix that checks the resumed client hello for the proper extension fields. Also manual testing has and a mach5 have been done to verify the fix as well as ensure that there are no side-effects. - Back ports: we wish to get this into 11.0.2 if possible.
20-10-2018

I added logs from the client that show the failure as well as a packet capture. The packet capture isn't useful for the same reasons that Brad mentioned in his earlier comment (the interesting stuff is encrypted), but you can see the client hello contains the server_name extension. The logs are more interesting. The high points: 21: The server_name extension in the initial ClientHello 374 and 385: Two NewSessionTicket messages sent by the server and consumed by the client 411-412: The ServerNameExtension.java producer code is unable to determine the server names (because it is an empty collection) 425-487: The extensions section for the resumed ClientHello, note the missing server_name extension
13-10-2018

This is a TLS 1.3 only issue. This happens when a NewSessionTicket is received by the Java client. The NST causes the creation of a new SSLSessionImpl object, but it is created from a PostHandshakeContext, not the ClientHandshakeContext that was used for most of the initial handshake. The PostHandshakeContext is not currently assigning any value to the requestedServerNames field, so when it is used to create a new SSLSessionImpl, the session's requestedServerNames field holds an empty collection. Upon resumption, that empty collection is found by the ServerNameExtension producer and the extension is omitted from the new ClientHello. This can be solved by making sure that the PostHandshakeContext at construction time obtains the list of server names from the TransportContext's conSession field (an SSLSessionImpl object). Once that is done, resumed sessions will populate the server_name extension in the client hello.
13-10-2018

The attached pcapng file shows the issue from the wire point-of-view. Note that we can't tell as much in a TLS1.3 connection as we could in a TLSv1.2, such as what certs were actually sent by the server. This shows the same as what the JSSE debug showed, except this includes the first connection and the timing of the TCP-level connection closes. Connection 1: ports 64734->443 Packet 18 opens the connection, then packet 22 sent a client hello with server_name indication. Packet 5979: Server sends a FIN. Client still sends some data (close_notify/more?) in 5983-85, then FINs also. Connection is completely closed. .05 seconds later, a brand new connection 2 opens between ports 64735->443 Packet 5987, 75.957 seconds in. Packet 5994 sends a client hello without a server_name indication, and a single pre_shared_key extension. Handshake seems to progress, but the connection fails. The client sends something, then sends a RST. ---------------------- This is stronger evidence to my guess that the second connection is losing the SNI data, and the server doesn't know which certificate to use. It doesn't appear that there is any https keep-alive level caching of connections here, but there is definitely the pre_shared_key caching going on on the second connection.
10-10-2018