2

下面是解包从 sslengine 接收到的数据包的代码:

private ByteBuffer doUnwrap() throws IOException {
        if (mPeerNetData.position() == 0) {
            // The network input buffer is empty; read data from the channel before doing the unwrap
            final int count = mSocketChannel.read(mPeerNetData);
            Log.d(TAG, "bytesRead : " + count);
            if (count == -1) {
                handleEndOfStream(mSocketChannel, mSslEngine);
                return null;
            }
        }

        Log.d(TAG, "isReadPending :" + isReadPending);
        if (!isReadPending) {
            mPeerNetData.flip();
        }

        final SSLEngineResult result;
        try {
            result = mSslEngine.unwrap(mPeerNetData, mPeerAppData);
        } catch (SSLException e) {
            Log.d(TAG, "Exception while calling SSLEngine.unwrap()" + e);
            shutdown();
            return null;
        }
        mPeerNetData.compact();
        Log.d(TAG, "Result of SSLEngine.unwrap(): {}" + result.getStatus());


        final SSLEngineResult.Status status = result.getStatus();
        switch (status) {
            case OK:
                if (mPeerNetData.position() != 0) {
                    isReadPending = true;
                    mPeerAppData = ensureRemaining(mPeerAppData, mSslEngine.getSession().getApplicationBufferSize());
                    doUnwrap();
                }
                break;

            case CLOSED:
                closeConnection(mSocketChannel, mSslEngine);
                break;

            case BUFFER_UNDERFLOW:
                // The network input buffer might not have enough space, re-allocate if necessary
                // (NOTE: packet buffer size as reported by the SSL session might change dynamically)
                mPeerNetData = ensureRemaining(mPeerNetData, mSslEngine.getSession().getPacketBufferSize());

                // Read data from the channel, retry unwrap if not end-of-stream
                final int count = mSocketChannel.read(mPeerNetData);
                if (count == -1) {
                    handleEndOfStream(mSocketChannel, mSslEngine);
                    return null;
                }
                doUnwrap();
                break;

            case BUFFER_OVERFLOW:
                // The application input buffer does not have enough space, re-allocate and retry unwrap
                // (NOTE: application buffer size as reported by the SSL session might change dynamically)
                mPeerAppData = ensureRemaining(mPeerAppData, mSslEngine.getSession().getApplicationBufferSize());
                doUnwrap();
                break;

            default:
                throw new IllegalStateException("Invalid SSL status: " + status);
        }

        return mPeerAppData;
    }

mPeerNetData 缓冲区在展开期间仍有一些数据要读取,因此我扩大了 mPeerAppData 缓冲区以保存在下一次​​迭代中必须从 mPeerNetData 缓冲区读取的更多数据。

我已经调试了流程,可以验证 mPeerAppData 缓冲区是否有足够的空间来保存数据,并且 mPeerNetData 缓冲区有待解包的数据。

但是在展开过程中,我收到以下错误:

javax.net.ssl.SSLException: Unable to parse TLS packet header
at org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:798)
at org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:738)

请建议我们如何避免这个错误?

4

1 回答 1

-1

在花了几天时间调试代码之后,我能够知道这个问题的根本原因。

在我发现在以下操作期间,上面的代码开始正常工作

result = mSslEngine.unwrap(mPeerNetData, mPeerAppData);

其中mPeerNetData是包含从中读取的加密字节的缓冲区,socketChannel并且mPeerAppData是在操作后保存解密字节的缓冲区unwrap

在 unwrap 操作成功解密来自 的数据包后mPeerNetData,仍有一些字节剩余,不足以unwrap再次执行操作。要进行进一步unwrap的操作,我们需要再次读取更多数据,socketChannel但请记住,我们不应该清除,mPeerNetData而是应该添加mPeerNetData之前留下一些字节的新数据。

那些先前的左字节必须在缓冲区中才能成功完整地解密数据,因为每个展开都会尝试从数据包中读取一些初始字节。这样,在下一次展开操作期间,操作将成功完成。

另请注意,要读取完整socketChannel数据,您应该有一些while loop逻辑来调用此方法,直到您从通道读取完整数据。

于 2020-08-14T15:07:53.900 回答