升级到 TLSv1.3 时 SSLEngine 使用的变化
Changes in SSLEngine usage when going up to TLSv1.3
Java 11 已发布 TLSv1.3
支持,默认使用。
它在 HTTPS 和 SSL 套接字的上下文中工作正常,但似乎在使用 SSLEngine
时由于 TLSv1.3
行为的变化而存在额外的障碍。
因此,使用 SSLEngine
通过 NIO
进行通信的稳健实现在启用 TLSv1.3
时不再有效。没有明显的错误,以异常或 SSL 错误的形式,两个节点只会来回发送 wrap/unwrap 消息并最终超时。
我对使用 TLSv1.2 的 SSLEngine 和使用 TLSv1.3 的 SSLEngine 之间行为变化的确切列表感兴趣,如果可能的话,我还想知道它们之间的迁移清单。不幸的是,Java 11 的 SSLEngine javadoc 没有此信息 - Java 11 中没有新方法,也没有对 TLSv1.3 的引用。
JDK11 的 Javadoc 中确实没有明确提及 TLS 1.3 对 SSLEngine
的影响,其方法也没有变化。
但是 the fifth item (closure) in the list of phases of SSLEngine
在 JDK 11 的 Javadoc 开头的一般描述中进行了更新:
Closure - When the connection is no longer needed, the client and the
server applications should each close both sides of their respective
connections. For SSLEngine
objects, an application should call
closeOutbound()
and send any remaining messages to the peer. Likewise,
an application should receive any remaining messages from the peer
before calling closeInbound()
. The underlying transport mechanism can
then be closed after both sides of the SSLEngine
have been closed. If
the connection is not closed in an orderly manner (for example
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 SSLEngine
must be created.
Oracle's Release Notes for JDK 11 中也讨论了更改:
TLS 1.3 Half-Close Policy
A new system property,
jdk.tls.acknowledgeCloseNotify
, has been 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 can occur if your application shuts down the
(D)TLS connection by 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.
Note that when a TLS/DTLS connection is no longer needed, the client
and server applications should each close both sides of their
respective connection.
因此,将 jdk.tls.acknowledgeCloseNotify
设置为 true 可能会解决您在将 SSlEngine
与 TLS 1.3 一起使用时对超时的具体担忧:
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.
发行说明文档还链接到已关闭的 JDK 错误 JDK-8208526 : TLS 1.3 half-close and synchronization issues,其中更详细地讨论了更改。
相关的(已关闭的)错误 JDK-8207009 : TLS 1.3 half-close and synchronization issues 也可能令人感兴趣。
其他参考资料:
- 请参阅 RFC 8446 的“附录 D. 向后兼容性”:“传输层安全 (TLS) 协议版本 1.3”(第 138-141 页)。
- 2:53:37 和 2:56:35 之间的 Oracle 视频“Monday Technical Sessions: Moscone West 2004”中简要讨论了 TLS 1.3 和早期版本之间的兼容性。
最后我们需要在握手完成后从buffer中读取剩余的数据,解包并更新握手状态。看起来像是我们之前没有处理的边缘情况。
Java 11 已发布 TLSv1.3
支持,默认使用。
它在 HTTPS 和 SSL 套接字的上下文中工作正常,但似乎在使用 SSLEngine
时由于 TLSv1.3
行为的变化而存在额外的障碍。
因此,使用 SSLEngine
通过 NIO
进行通信的稳健实现在启用 TLSv1.3
时不再有效。没有明显的错误,以异常或 SSL 错误的形式,两个节点只会来回发送 wrap/unwrap 消息并最终超时。
我对使用 TLSv1.2 的 SSLEngine 和使用 TLSv1.3 的 SSLEngine 之间行为变化的确切列表感兴趣,如果可能的话,我还想知道它们之间的迁移清单。不幸的是,Java 11 的 SSLEngine javadoc 没有此信息 - Java 11 中没有新方法,也没有对 TLSv1.3 的引用。
JDK11 的 Javadoc 中确实没有明确提及 TLS 1.3 对 SSLEngine
的影响,其方法也没有变化。
但是 the fifth item (closure) in the list of phases of SSLEngine
在 JDK 11 的 Javadoc 开头的一般描述中进行了更新:
Closure - When the connection is no longer needed, the client and the server applications should each close both sides of their respective connections. For
SSLEngine
objects, an application should callcloseOutbound()
and send any remaining messages to the peer. Likewise, an application should receive any remaining messages from the peer before callingcloseInbound()
. The underlying transport mechanism can then be closed after both sides of theSSLEngine
have been closed. If the connection is not closed in an orderly manner (for examplecloseInbound()
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 newSSLEngine
must be created.
Oracle's Release Notes for JDK 11 中也讨论了更改:
TLS 1.3 Half-Close Policy
A new system property,jdk.tls.acknowledgeCloseNotify
, has been added. The default value of the system property is false. If the system property is set to true, a correspondingclose_notify alert
will be sent when receiving aclose_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 can occur if your application shuts down the (D)TLS connection by using only one of the
SSLEngine.closeInbound()
orSSLEngine.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.Note that when a TLS/DTLS connection is no longer needed, the client and server applications should each close both sides of their respective connection.
因此,将 jdk.tls.acknowledgeCloseNotify
设置为 true 可能会解决您在将 SSlEngine
与 TLS 1.3 一起使用时对超时的具体担忧:
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.
发行说明文档还链接到已关闭的 JDK 错误 JDK-8208526 : TLS 1.3 half-close and synchronization issues,其中更详细地讨论了更改。
相关的(已关闭的)错误 JDK-8207009 : TLS 1.3 half-close and synchronization issues 也可能令人感兴趣。
其他参考资料:
- 请参阅 RFC 8446 的“附录 D. 向后兼容性”:“传输层安全 (TLS) 协议版本 1.3”(第 138-141 页)。
- 2:53:37 和 2:56:35 之间的 Oracle 视频“Monday Technical Sessions: Moscone West 2004”中简要讨论了 TLS 1.3 和早期版本之间的兼容性。
最后我们需要在握手完成后从buffer中读取剩余的数据,解包并更新握手状态。看起来像是我们之前没有处理的边缘情况。