JDK-8208526 : TLS 1.3 half-close and synchronization issues
  • Type: CSR
  • Component: security-libs
  • Sub-Component: javax.net.ssl
  • Priority: P2
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 11
  • Submitted: 2018-07-30
  • Updated: 2020-07-03
  • Resolved: 2018-08-14
Related Reports
CSR :  
Relates :  
Sub Tasks
JDK-8208538 :  
Description
Summary
-------
Provide a workaround if an application does not work well with the TLS 1.3 half-close policy.

Problem
-------
Unlike TLS 1.2 and prior versions, TLS 1.3 uses a half-close policy.  In TLS 1.3, the inbound and outbound close_notify alerts are independent.  While in TLS1.2 and prior versions, if one party closes its write side by sending the close notify alert  (closeOutbound) , then it MUST receive a close_notify alert response from the peer and if the peer has closed its write side of connection by sending a close notify alert, then upon receipt of the alert, the party MUST send a close notify alert response.

In practice, an application may only close outbound even if it intends to close the inbound as well, or close the connection completely.  These use cases work for TLS 1.2 and prior versions,  but not for TLS 1.3 because of the TLS 1.3 specification changes.  With TLS 1.3, the application may hang waiting for further operations.  This issue could be solved by the application explicitly closing both inbound and outbound in each side of the connection, but that requires source code updates which may not be practical for many applications.

Solution
--------

In order to mitigate the impact, a new System Property, "jdk.tls.acknowledgeCloseNotify", is introduced.  If the System Property is set to "true", when receiving the close_notify alert, a corresponding close_notify alert will be sent, and the connection can be duplex closed accordingly.  This is a JDK specific property that will be supported by the SunJSSE implementation.

Specification
-------------
**Add a new System Property:**

A new System Property, "jdk.tls.acknowledgeCloseNotify", is added.  The default value of the System Property is "false".  If the System Property is set to "true", a corresponding close_notify alert will be sent when receiving a close_notify alert, and the connection will be duplex closed.

TLS 1.2 and prior versions use a duplex-close policy, while TLS 1.3 uses a half-close policy.   The inbound and the outbound close_notify alerts for TLS 1.3 are independent.  When upgrading to TLS 1.3, unexpected behavior may occur if your application shuts down the (D)TLS connection using only one of the SSLEngine.closeInbound() or SSLEngine.closeOutbound() APIs, but not both in each side of the connection.  If your application exhibits unexpected hangs or timeouts when the underlying (D)TLS transportation is not duplex closed, you may need to set this property to true. 


**Add a SSLSocket apiNote;**

@apiNote When the connection is no longer needed, the client and server applications should each close both sides of their respective connection.  For {@code SSLSocket} objects, for example, an application can call {@link Socket#shutdownOutput} or {@link OutputStream#close} to close the output stream and call {@link Socket#shutdownInput} or {@link InputStream#close} to close the input stream.  Note that in some cases, closing the input stream may depend on the peer's output stream being closed first.  If the connection is not closed in an orderly manner (for example {@link Socket#shutdownInput} is called before the peer's write closure notification has been received), exceptions may be raised to indicate that an error has occurred.  Once an {@code SSLSocket} is closed, it is not reusable: a new {@code SSLSocket} must be created. 


**Update the closure note in SSLEngine:**

Replace:

Closure - When the connection is no longer needed, the application should close the {@code SSLEngine} and should send/receive any remaining messages to the peer before closed, it is not reusable:  a new {@code SSLEngine} must be created.

with:

Closure - When the connection is no longer needed, the client and the server applications should each close both sides of their
respective connections.  For {@code SSLEngine} objects, an application should call {@link SSLEngine#closeOutbound} and
send any remaining messages to the peer.  Likewise, an application should receive any remaining messages from the peer before
calling {@link SSLEngine#closeInbound}.  The underlying transport mechanism can then be closed after both sides of the
{@code SSLEngine} have been closed.  If the connection is not closed in an orderly manner (for example {@link SSLEngine#closeInbound} is called before the peer's write closure notification has been received), exceptions will be raised
to indicate that an error has occurred.  Once an engine is closed, it is not reusable: a new {@code SSLEngine} must be created.


Compatibility Risk
-------------
Low.
Comments
Moving to Approved.
14-08-2018

Here is the webrev for the update: http://cr.openjdk.java.net/~xuelei/8207009/webrev.06/ See src/java.base/share/classes/javax/net/ssl/SSLEngine.java and src/java.base/share/classes/javax/net/ssl/SSLSocket.java for the specification clarification.
14-08-2018

Updated the class descriptions of SSLEngine and SSLSocket so that users have a better sense about how to close a TLS connection properly.
13-08-2018

This is my understanding of the situation: Under TLS 1.2 and earlier, if an application only explicitly closed one of the inbound and outbound connections, the other would implicitly get closed. Under TLS 1.3, such implicit closes do *not* occur anymore, creating the possibility of applications getting hung with open but not semantically active connections. The new system property proposed to opt-in to the implicitly closing behavior in case applications get tripped up by the new TLS behavior. Is that correct? If so, it would seem to me to be prudent to work in an advisory to "make sure to close both inbound and outbound connections!" somewhere into the javadoc.
10-08-2018

> As of JDK 11, what is the correct/recommended why to use these APIs on the context of TLS 1.3? > > If the recommend usage has changed, perhaps that should be discussed in the API docs and the already-planned release note. No recommend usage change, I think. As before, applications should close both inbound and outbound per the current specification. This System Property acts as a workaround if an application does not follow the spec, but it just works because of the protocol design of TLS 1.2 and prior versions. A known issue may be added to the JSSE Reference Guide so that customers can aware the issue if they don't follow the spec strictly.
08-08-2018

As of JDK 11, what is the correct/recommended why to use these APIs on the context of TLS 1.3? If the recommend usage has changed, perhaps that should be discussed in the API docs and the already-planned release note.
08-08-2018

> I was expecting to see some kind of API update as part of this change. There is already an API workaround. If an application close the both inbound and outbound (SSLEngine.closeInbound/closeOutbound, Socket.shutdownInput/shutdownOutput, InputStream/OutputStream.close()), it should be safe. The System Property is added for those applications that close only inbound or only outbound currently. It is a problem of the application, but it works because previously TLS uses duplex-close policy. If these applications upgrade to TLS 1.3, the problem appears as the connection can only be half-closed. it would be nice if the System Property can help the upgrade if application source code update is not available. The current involved half-close APIs include: . InputStream.close() . OutputStream.close() . Socket.shutdownInput() . Socket.shutdownOutput() . SSLEngine.closeInbound() . SSLEngine.closeOutbound() And the duplex-close API is: . Socket.close(). The API specifications are sufficient. We have no plan to update the APIs specification.
06-08-2018

I was expecting to see some kind of API update as part of this change.
03-08-2018