java 通过安全连接(TLSv2)处理TCP损坏数据包失败,因为我无法看到或解密内容

klsxnrf1  于 2023-01-07  发布在  Java
关注(0)|答案(1)|浏览(142)

我正在开发一个客户端应用程序,它通过TLS连接向服务器发送事务。应用程序发送给定数量的字节,并接收1182字节作为响应。在我开始增加每秒的事务数量之前,它一直工作正常。在那之后,一些响应数据包开始被破坏,并且不能被客户端完全接收。2当我试图打开数据包内容时,它引发了一个异常并终止了TLS会话。

javax.net.ssl.SSLException: Unrecognized record version (D)TLS-0.0 , plaintext connection?
    at java.base/sun.security.ssl.SSLEngineInputRecord.bytesInCompletePacket(SSLEngineInputRecord.java:98)
    at java.base/sun.security.ssl.SSLEngineInputRecord.bytesInCompletePacket(SSLEngineInputRecord.java:64)
    at java.base/sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:544)
    at java.base/sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:441)
    at java.base/sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:420)
    at java.base/javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:634)
    at MyClass.handleEncryptedTransaction(MyClass.java:297)

我尝试使用缓冲区来累积可能损坏的包,但是在解封装之前我无法看到包的内容,只能根据包的大小来估计包是否完整。

this.peerNetData.clear();
    int bytesRead = socketChannel.read(this.peerNetData);
    if (bytesRead == DEFAULT_TLS_HANDSHAKE_SIZE) {
        handleEncryptedTransaction(socketChannel, engine);
    } else if (bytesRead > 0) {
        // TLS packet buffering

        byte[] justRead = new byte[this.peerNetData.position()];
        this.peerNetData.flip();
        this.peerNetData.get(justRead);
        this.incompleteTransactionsBuffer.put(justRead);

        // DEFAULT_TLS_TRANSACTION_SIZE == 1182
        if (this.incompleteTransactionsBuffer.position() >= DEFAULT_TLS_TRANSACTION_SIZE) {
            this.incompleteTransactionsBuffer.flip(); // flipping to read mode

            while (this.incompleteTransactionsBuffer.remaining() >= DEFAULT_TLS_TRANSACTION_SIZE) {
                byte[] fullTransaction = new byte[DEFAULT_TLS_TRANSACTION_SIZE];

                this.incompleteTransactionsBuffer.get(fullTransaction, 0, fullTransaction.length);

                this.peerNetData.clear();
                this.peerNetData.put(fullTransaction);

                // This method uses this.peerNetData to unwrap data and 
                handleEncryptedTransaction(socketChannel, engine);
            }
            this.incompleteTransactionsBuffer.compact(); // wipe out bytes that had been read and free space of the buffer
        }
    }

**是否有任何方法可以检查TLS上的TCP数据包是否完整?**我尝试读取前1182个字节,但似乎不起作用。有趣的是,当我在响应中获得多个数据包时,此代码起作用,例如(N * 1182),其中N从2到7不等。也许我应该等待另一个套接字读取并获得另一条信息?

我想这个问题是因为流量过大导致的数据包重传而引起的。在Java中,是否有其他方法可以处理低级套接字连接中的TLS数据包重传?

eit6fx6z

eit6fx6z1#

在得到评论和更好地理解TLS协议之后,我可以通过实现缓冲区并获得TLS记录的确切大小来等待其他TCP读取来找到问题的解决方案。
一个TLS记录“可能被分割成多个TCP片段,或者TCP片段也可能包含多个完整的、一半的或其他的TLS记录。这些TCP片段甚至可能被分割成多个IP数据包,尽管TCP努力避免这种情况。",来自Determine packet size of TLS packet Java/Android。然而,该帖子提到了前2个字节,这是不正确的。根据https://hpbn.co/transport-layer-security-tls/#tls-record-diagram:
最大TLS记录大小为16 KB

每条记录包含一个5字节的报头、一个MAC(SSLv3、TLS 1.0、TLS 1.1最多20字节,TLS 1.2最多32字节)和填充(如果使用块密码)。
要解密和验证记录,必须提供整个记录。

TLS记录大小取决于第3和第4字节:

代码最后是这样的:

protected synchronized void read(SocketChannel socketChannel, SSLEngine engine) throws IOException {

    this.peerNetData.clear();
    int bytesRead = socketChannel.read(this.peerNetData);
    if (bytesRead > 0) {
        // TLS records buffering
        this.peerNetData.flip();
        byte[] justRead = new byte[this.peerNetData.limit()];
        this.peerNetData.get(justRead);
        this.tlsRecordsReadBuffer.put(justRead);
        byte[] fullTlsRecord;

        // Process every TLS record available until buffer is empty or the last record is yet not complete
        while ( (fullTlsRecord = this.getFullTlsRecordFromBufferAndDeleteIt()) != null ) {
                this.peerNetData.clear();
                this.peerNetData.put(fullTlsRecord);
                handleEncryptedTransaction(socketChannel, engine);
        }
    } else if (bytesRead < 0) {
        handleEndOfStream(socketChannel, engine);
    }
}

private synchronized byte[] getFullTlsRecordFromBufferAndDeleteIt() {
    byte[] result = null;
    this.tlsRecordsReadBuffer.flip();

    if (this.tlsRecordsReadBuffer.limit() > DEFAULT_TLS_HEADER_SIZE) {
        // Read only the first 5 bytes (5 = DEFAULT_TLS_HEADER_SIZE) which contains TLS record length
        byte[] tlsHeader = new byte[DEFAULT_TLS_HEADER_SIZE];
        this.tlsRecordsReadBuffer.get(tlsHeader);

        **// read 3rd and 4th bytes to get TLS record length in big endian notation
        int tlsRecordSize = ((tlsHeader[3] & 0xff) << 8) | (tlsHeader[4] & 0xff);**
        // 0xff IS NECESSARY because it removes one-bit negative representation
        // Set  position back to the beginning
        this.tlsRecordsReadBuffer.position(0);
        if (this.tlsRecordsReadBuffer.limit() >= (tlsRecordSize + DEFAULT_TLS_HEADER_SIZE)) {
            // Then we have a complete TLS record
            result = new byte[tlsRecordSize + DEFAULT_TLS_HEADER_SIZE];
            this.tlsRecordsReadBuffer.get(result);
        }
    }

    // remove record and get back to write mode
    this.tlsRecordsReadBuffer.compact();
    return result;
}

相关问题