java SSLEngine 说 NEED_WRAP,调用 .wrap() 仍然 NEED_WRAP
java SSLEngine says NEED_WRAP, call .wrap() and still NEED_WRAP
我发现 SSLEngine 有一个奇怪的问题,想知道是我的代码还是 SSLEngine 有问题。这是我看东西的顺序
- 握手状态为NEED_WRAP
- 我们叫SSLEngine.WRAP
- 之后,缓冲区中写入了零数据,并且 SSLEngineResult.result=OK(既没有溢出也没有下溢 :( ) 并且 HandshakeStatus 仍然是 NEED_WRAP
最重要的问题:如何彻底调试?如何以某种方式 'see' 每条消息?我可以很容易地捕获字节流,但是是否有一些库可以将其解析为 SSL 握手 objects?
第298行(记录之前的握手状态)到第328行(我们抛出带有信息的异常)是这里的相关代码
堆栈跟踪是
2019-06-21 08:58:24,562 [-] [webpiecesThreadPool6] Caller+1 at org.webpieces.util.threading.SessionExecutorImpl$RunnableWithKey.run(SessionExecutorImpl.java:123)
ERROR: Uncaught Exception
java.lang.IllegalStateException: Engine issue. hsStatus=NEED_WRAP status=OK previous hsStatus=NEED_WRAP
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.sendHandshakeMessageImpl(AsyncSSLEngine3Impl.java:328)
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.sendHandshakeMessage(AsyncSSLEngine3Impl.java:286)
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.doHandshakeWork(AsyncSSLEngine3Impl.java:133)
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.doHandshakeLoop(AsyncSSLEngine3Impl.java:246)
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.unwrapPacket(AsyncSSLEngine3Impl.java:210)
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.doWork(AsyncSSLEngine3Impl.java:109)
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.feedEncryptedPacket(AsyncSSLEngine3Impl.java:82)
at org.webpieces.nio.impl.ssl.SslTCPChannel$SocketDataListener.incomingData(SslTCPChannel.java:175)
at org.webpieces.nio.impl.threading.ThreadDataListener.run(ThreadDataListener.java:26)
at org.webpieces.util.threading.SessionExecutorImpl$RunnableWithKey.run(SessionExecutorImpl.java:121)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
有什么想法吗?我怎样才能真正深入研究这个问题?我的偏好是一个库,它接受字节并吐出代表每个握手消息或解密数据包的 ssl objects(带有原始加密事物附带的任何 header 信息)。
具体来说就是上面提到的代码
HandshakeStatus previousStatus = sslEngine.getHandshakeStatus();
//CLOSE and all the threads that call feedPlainPacket can have contention on wrapping to encrypt and
//must synchronize on sslEngine.wrap
Status lastStatus;
HandshakeStatus hsStatus;
synchronized (wrapLock ) {
HandshakeStatus beforeWrapHandshakeStatus = sslEngine.getHandshakeStatus();
if (beforeWrapHandshakeStatus != HandshakeStatus.NEED_WRAP)
throw new IllegalStateException("we should only be calling this method when hsStatus=NEED_WRAP. hsStatus=" + beforeWrapHandshakeStatus);
//KEEEEEP This very small. wrap and then listener.packetEncrypted
SSLEngineResult result = sslEngine.wrap(SslMementoImpl.EMPTY, engineToSocketData);
lastStatus = result.getStatus();
hsStatus = result.getHandshakeStatus();
}
log.trace(()->mem+"write packet pos="+engineToSocketData.position()+" lim="+
engineToSocketData.limit()+" status="+lastStatus+" hs="+hsStatus);
if(lastStatus == Status.BUFFER_OVERFLOW || lastStatus == Status.BUFFER_UNDERFLOW)
throw new RuntimeException("status not right, status="+lastStatus+" even though we sized the buffer to consume all?");
boolean readNoData = engineToSocketData.position() == 0;
engineToSocketData.flip();
try {
CompletableFuture<Void> sentMsgFuture;
if(readNoData) {
log.trace(() -> "ssl engine is farting. READ 0 data. hsStatus="+hsStatus+" status="+lastStatus);
throw new IllegalStateException("Engine issue. hsStatus="+hsStatus+" status="+lastStatus+" previous hsStatus="+previousStatus);
//A big hack since the Engine was not working in live testing with FireFox and it would tell us to wrap
//and NOT output any data AND not BufferOverflow.....you have to do 1 or the other, right!
//instead cut out of looping since there seems to be no data
//sslEngineIsFarting = true;
//sentMsgFuture = CompletableFuture.completedFuture(null);
谢谢,
院长
好吧,这越来越像是一个 jdk 错误,现在似乎证明了这一点。我的日志显示我在 jdk11.0.3(在 MAC)
2019-06-29 23:37:18,793 [main][] [-] Caller+1 at
WEBPIECESxPACKAGE.DevelopmentServer.main(DevelopmentServer.java:32)
INFO: Starting Development Server under java version=11.0.3
我使用了
的调试 ssl 标志设置
-Djavax.net.debug=ssl:handshake:verbose:keymanager:trustmanager -Djava.security.debug=access:stack
我也用自己的日志包围了对 sslEngine.wrap 和 sslEngine.unwrap 的所有调用,幸运的是每个人的日志中也都有线程名称。
似乎当客户发送警告时
javax.net.ssl|DEBUG|14|webpiecesThreadPool1|2019-06-29 23:27:14.860
MDT|Alert.java:232|Received alert message (
"Alert": {
"level" : "warning",
"description": "close_notify"
}
)
SSLEngine 告诉我们需要 WRAP,这是正确的,因为我们还需要用 close_notify 回复以防止截断攻击但是引擎 returns 0 字节并告诉我们引擎的状态仍然是 NEED_WRAP。
此外,在 sslEngine.wrap 调用之前和之后我的日志之间有来自 java 的零 ssl 调试日志,几乎就像 sslEngine 放屁一样什么也没做。
然后,我认为我会很酷,并将其添加为 main() 方法的第一行
System.setProperty("jdk.tls.server.protocols", "TLSv1.2");
System.setProperty("jdk.tls.client.protocols", "TLSv1.2");
但是在调试信息中选择的版本仍然是 TLSv1.3....grrrrrr...哦,我放弃了。
哦,更好,降级到 1.8。0_111 成功了
2019-06-30 00:11:54,813 [main] Caller+1 at
WEBPIECESxPACKAGE.DevelopmentServer.main(DevelopmentServer.java:32)
INFO: Starting Development Server under java version=1.8.0_111
webpiecesThreadPool5, READ: TLSv1.2 Alert, length = 26
webpiecesThreadPool2, RECV TLSv1.2 ALERT: warning, close_notify
webpiecesThreadPool2, closeInboundInternal()
webpiecesThreadPool2, closeOutboundInternal()
webpiecesThreadPool5, RECV TLSv1.2 ALERT: warning, close_notify
webpiecesThreadPool5, closeInboundInternal()
webpiecesThreadPool5, closeOutboundInternal()
webpiecesThreadPool2, SEND TLSv1.2 ALERT: warning, description = close_notify
webpiecesThreadPool5, SEND TLSv1.2 ALERT: warning, description = close_notify
webpiecesThreadPool2, WRITE: TLSv1.2 Alert, length = 26
webpiecesThreadPool5, WRITE: TLSv1.2 Alert, length = 26
System.setProperty("jdk.tls.server.protocols", "TLSv1.2");
System.setProperty("jdk.tls.client.protocols", "TLSv1.2");
系统 属性 应该在加载 JSSE 之前设置。例如,在命令行中设置 属性。是系统属性不适合你的原因吗?
... the SSLEngine tells us we need to WRAP which is correct as we need to be replying with a close_notify as well to prevent truncation attacks BUT instead the engine returns 0 bytes and tells us the state of the engine is still NEED_WRAP.
TLS 1.3 使用半关闭策略(参见 RFC 8446)。当收到close_notify时,入站端关闭,出站端保持开启。本端收不到任何数据,但允许发送更多的应用数据。
半关闭策略对兼容性有一些影响(参见 JDK 11 发行说明,https://www.oracle.com/technetwork/java/javase/11-relnote-issues-5012449.html)。系统 属性、"jdk.tls.acknowledgeCloseNotify" 可用作解决方法。有关详细信息,请参阅 JDK 11 发行说明。
我发现 SSLEngine 有一个奇怪的问题,想知道是我的代码还是 SSLEngine 有问题。这是我看东西的顺序
- 握手状态为NEED_WRAP
- 我们叫SSLEngine.WRAP
- 之后,缓冲区中写入了零数据,并且 SSLEngineResult.result=OK(既没有溢出也没有下溢 :( ) 并且 HandshakeStatus 仍然是 NEED_WRAP
最重要的问题:如何彻底调试?如何以某种方式 'see' 每条消息?我可以很容易地捕获字节流,但是是否有一些库可以将其解析为 SSL 握手 objects?
第298行(记录之前的握手状态)到第328行(我们抛出带有信息的异常)是这里的相关代码
堆栈跟踪是
2019-06-21 08:58:24,562 [-] [webpiecesThreadPool6] Caller+1 at org.webpieces.util.threading.SessionExecutorImpl$RunnableWithKey.run(SessionExecutorImpl.java:123)
ERROR: Uncaught Exception
java.lang.IllegalStateException: Engine issue. hsStatus=NEED_WRAP status=OK previous hsStatus=NEED_WRAP
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.sendHandshakeMessageImpl(AsyncSSLEngine3Impl.java:328)
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.sendHandshakeMessage(AsyncSSLEngine3Impl.java:286)
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.doHandshakeWork(AsyncSSLEngine3Impl.java:133)
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.doHandshakeLoop(AsyncSSLEngine3Impl.java:246)
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.unwrapPacket(AsyncSSLEngine3Impl.java:210)
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.doWork(AsyncSSLEngine3Impl.java:109)
at org.webpieces.ssl.impl.AsyncSSLEngine3Impl.feedEncryptedPacket(AsyncSSLEngine3Impl.java:82)
at org.webpieces.nio.impl.ssl.SslTCPChannel$SocketDataListener.incomingData(SslTCPChannel.java:175)
at org.webpieces.nio.impl.threading.ThreadDataListener.run(ThreadDataListener.java:26)
at org.webpieces.util.threading.SessionExecutorImpl$RunnableWithKey.run(SessionExecutorImpl.java:121)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
有什么想法吗?我怎样才能真正深入研究这个问题?我的偏好是一个库,它接受字节并吐出代表每个握手消息或解密数据包的 ssl objects(带有原始加密事物附带的任何 header 信息)。
具体来说就是上面提到的代码
HandshakeStatus previousStatus = sslEngine.getHandshakeStatus();
//CLOSE and all the threads that call feedPlainPacket can have contention on wrapping to encrypt and
//must synchronize on sslEngine.wrap
Status lastStatus;
HandshakeStatus hsStatus;
synchronized (wrapLock ) {
HandshakeStatus beforeWrapHandshakeStatus = sslEngine.getHandshakeStatus();
if (beforeWrapHandshakeStatus != HandshakeStatus.NEED_WRAP)
throw new IllegalStateException("we should only be calling this method when hsStatus=NEED_WRAP. hsStatus=" + beforeWrapHandshakeStatus);
//KEEEEEP This very small. wrap and then listener.packetEncrypted
SSLEngineResult result = sslEngine.wrap(SslMementoImpl.EMPTY, engineToSocketData);
lastStatus = result.getStatus();
hsStatus = result.getHandshakeStatus();
}
log.trace(()->mem+"write packet pos="+engineToSocketData.position()+" lim="+
engineToSocketData.limit()+" status="+lastStatus+" hs="+hsStatus);
if(lastStatus == Status.BUFFER_OVERFLOW || lastStatus == Status.BUFFER_UNDERFLOW)
throw new RuntimeException("status not right, status="+lastStatus+" even though we sized the buffer to consume all?");
boolean readNoData = engineToSocketData.position() == 0;
engineToSocketData.flip();
try {
CompletableFuture<Void> sentMsgFuture;
if(readNoData) {
log.trace(() -> "ssl engine is farting. READ 0 data. hsStatus="+hsStatus+" status="+lastStatus);
throw new IllegalStateException("Engine issue. hsStatus="+hsStatus+" status="+lastStatus+" previous hsStatus="+previousStatus);
//A big hack since the Engine was not working in live testing with FireFox and it would tell us to wrap
//and NOT output any data AND not BufferOverflow.....you have to do 1 or the other, right!
//instead cut out of looping since there seems to be no data
//sslEngineIsFarting = true;
//sentMsgFuture = CompletableFuture.completedFuture(null);
谢谢, 院长
好吧,这越来越像是一个 jdk 错误,现在似乎证明了这一点。我的日志显示我在 jdk11.0.3(在 MAC)
2019-06-29 23:37:18,793 [main][] [-] Caller+1 at WEBPIECESxPACKAGE.DevelopmentServer.main(DevelopmentServer.java:32) INFO: Starting Development Server under java version=11.0.3
我使用了
的调试 ssl 标志设置-Djavax.net.debug=ssl:handshake:verbose:keymanager:trustmanager -Djava.security.debug=access:stack
我也用自己的日志包围了对 sslEngine.wrap 和 sslEngine.unwrap 的所有调用,幸运的是每个人的日志中也都有线程名称。
似乎当客户发送警告时
javax.net.ssl|DEBUG|14|webpiecesThreadPool1|2019-06-29 23:27:14.860
MDT|Alert.java:232|Received alert message (
"Alert": {
"level" : "warning",
"description": "close_notify"
}
)
SSLEngine 告诉我们需要 WRAP,这是正确的,因为我们还需要用 close_notify 回复以防止截断攻击但是引擎 returns 0 字节并告诉我们引擎的状态仍然是 NEED_WRAP。
此外,在 sslEngine.wrap 调用之前和之后我的日志之间有来自 java 的零 ssl 调试日志,几乎就像 sslEngine 放屁一样什么也没做。
然后,我认为我会很酷,并将其添加为 main() 方法的第一行
System.setProperty("jdk.tls.server.protocols", "TLSv1.2");
System.setProperty("jdk.tls.client.protocols", "TLSv1.2");
但是在调试信息中选择的版本仍然是 TLSv1.3....grrrrrr...哦,我放弃了。
哦,更好,降级到 1.8。0_111 成功了
2019-06-30 00:11:54,813 [main] Caller+1 at
WEBPIECESxPACKAGE.DevelopmentServer.main(DevelopmentServer.java:32)
INFO: Starting Development Server under java version=1.8.0_111
webpiecesThreadPool5, READ: TLSv1.2 Alert, length = 26
webpiecesThreadPool2, RECV TLSv1.2 ALERT: warning, close_notify
webpiecesThreadPool2, closeInboundInternal()
webpiecesThreadPool2, closeOutboundInternal()
webpiecesThreadPool5, RECV TLSv1.2 ALERT: warning, close_notify
webpiecesThreadPool5, closeInboundInternal()
webpiecesThreadPool5, closeOutboundInternal()
webpiecesThreadPool2, SEND TLSv1.2 ALERT: warning, description = close_notify
webpiecesThreadPool5, SEND TLSv1.2 ALERT: warning, description = close_notify
webpiecesThreadPool2, WRITE: TLSv1.2 Alert, length = 26
webpiecesThreadPool5, WRITE: TLSv1.2 Alert, length = 26
System.setProperty("jdk.tls.server.protocols", "TLSv1.2");
System.setProperty("jdk.tls.client.protocols", "TLSv1.2");
系统 属性 应该在加载 JSSE 之前设置。例如,在命令行中设置 属性。是系统属性不适合你的原因吗?
... the SSLEngine tells us we need to WRAP which is correct as we need to be replying with a close_notify as well to prevent truncation attacks BUT instead the engine returns 0 bytes and tells us the state of the engine is still NEED_WRAP.
TLS 1.3 使用半关闭策略(参见 RFC 8446)。当收到close_notify时,入站端关闭,出站端保持开启。本端收不到任何数据,但允许发送更多的应用数据。
半关闭策略对兼容性有一些影响(参见 JDK 11 发行说明,https://www.oracle.com/technetwork/java/javase/11-relnote-issues-5012449.html)。系统 属性、"jdk.tls.acknowledgeCloseNotify" 可用作解决方法。有关详细信息,请参阅 JDK 11 发行说明。