6

我看到 SSLEngine 有一个奇怪的问题,想知道我的代码或 SSLEngine 是否有问题。这是我看到事物的顺序

  1. HandshakeStatus 是 NEED_WRAP
  2. 我们调用 SSLEngine.WRAP
  3. 之后,有零数据写入缓冲区,并且 SSLEngineResult.result=OK(not overflow or underflow :( ) 并且 HandshakeStatus 是 STILL NEED_WRAP

最重要的问题:如何彻底调试?如何以某种方式“看到”每条消息?我可以很容易地捕获字节流,但是是否有一些库可以将其解析为 SSL 握手对象?

第 298 行(记录之前的握手状态)到第 328 行(我们用信息抛出异常)是这里的相关代码

https://github.com/deanhiller/webpieces/blob/sslEngineFartingExample/core/core-ssl/src/main/java/org/webpieces/ssl/impl/AsyncSSLEngine3Impl.java

堆栈跟踪是

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$1.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 对象(带有原始加密事物附带的任何标头信息)。

具体来说,这里是上面提到的代码

        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);

谢谢,院长

4

3 回答 3

1

好吧,这似乎越来越像一个 jdk 错误,现在这似乎证明了这一点。我的日志显示我在 jdk11.0.3(在 MAC 上)

2019-06-29 23:37:18,793 [main][] [-] Caller+1 at WEBPIECESxPACKAGE.DevelopmentServer.main(DevelopmentServer.java:32) 信息:在 java 版本 = 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 进行回复以防止截断攻击,但是引擎返回 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...哦,我放弃了。

于 2019-06-30T05:42:30.370 回答
1

哦,更好的是,降级到 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
于 2019-06-30T06:15:24.570 回答
1

System.setProperty("jdk.tls.server.protocols", "TLSv1.2");

System.setProperty("jdk.tls.client.protocols", "TLSv1.2");

应该在加载 JSSE 之前设置系统属性。例如,在命令行中设置属性。是系统属性对您不起作用的原因吗?

... SSLEngine 告诉我们需要 WRAP,这是正确的,因为我们还需要使用 close_notify 进行回复以防止截断攻击,但是引擎返回 0 字节并告诉我们引擎的状态仍然是 NEED_WRAP。

TLS 1.3 使用半关闭策略(参见 RFC 8446)。当收到 close_notify 时,inbound 端将关闭,outbound 端保持打开状态。本端不能接收任何数据,但允许发送更多的应用数据。

半关闭策略对兼容性有一些影响(请参阅 JDK 11 发行说明,https://www.oracle.com/technetwork/java/javase/11-relnote-issues-5012449.html)。系统属性“jdk.tls.acknowledgeCloseNotify”可用作解决方法。有关详细信息,请参阅 JDK 11 发行说明。

于 2019-09-16T15:17:03.177 回答