Netty SSL 中的字段长度溢出



我使用 Netty 来实现带有安全套接字的服务器。我的 sslHandler 代码是:

SslHandler sslHandler = SslContextBuilder
.forServer(certFile, keyFile)
.trustManager(trustFile)
.clientAuth(ClientAuth.REQUIRE)
.build()
.newHandler(channel.alloc());

trustFile是一个 File 对象,其中包含大约 700 条证书文本,例如:

-----开始证书----- MIIEHDCCAwSgAwIBAgIJAOR6+3G8C6f7MA0GCSqGSIb3DQEBCwUAMIGNMQswCQYD VQQGEwJVUzESMBAGA1UECAwJQ2FsaWZvbWlhMRwwGgYDVQQKDBNDaXNjbyBTeXN0 ................................................................ igHdyc519KbYSMfhuM9gXw35LPmFWStBGYikBcMZJ1WmWxb/eZOK1SMjVQ/L/JVg -----结束证书

-----当我连接服务器时

curl -k -v -E client.pem --key client.key.pem --cacert rootCA.pem https://10.140.28.33:31069

弹出一个异常:

11:00:18.636 [nioEventLoopGroup-3-2] WARN io.netty.channel.DefaultChannelPipeline - An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.handler.codec.DecoderException: java.lang.RuntimeException: Field length overflow, the field length (106142) should be less than 65536
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:459)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:579)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:496)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.RuntimeException: Field length overflow, the field length (106142) should be less than 65536
at sun.security.ssl.Handshaker.checkThrown(Handshaker.java:1476)
at sun.security.ssl.SSLEngineImpl.checkTaskThrown(SSLEngineImpl.java:535)
at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:813)
at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:781)
at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
at io.netty.handler.ssl.SslHandler$SslEngineType$3.unwrap(SslHandler.java:255)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1162)
at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1084)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428)
... 16 common frames omitted
Caused by: java.lang.RuntimeException: Field length overflow, the field length (106142) should be less than 65536
at sun.security.ssl.HandshakeOutStream.checkOverflow(HandshakeOutStream.java:231)
at sun.security.ssl.HandshakeOutStream.putInt16(HandshakeOutStream.java:163)
at sun.security.ssl.HandshakeMessage$CertificateRequest.send(HandshakeMessage.java:1442)
at sun.security.ssl.HandshakeMessage.write(HandshakeMessage.java:143)
at sun.security.ssl.ServerHandshaker.clientHello(ServerHandshaker.java:971)
at sun.security.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:224)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1026)
at sun.security.ssl.Handshaker$1.run(Handshaker.java:966)
at sun.security.ssl.Handshaker$1.run(Handshaker.java:963)
at java.security.AccessController.doPrivileged(Native Method)
at sun.security.ssl.Handshaker$DelegatedTask.run(Handshaker.java:1416)
at io.netty.handler.ssl.SslHandler.runDelegatedTasks(SslHandler.java:1301)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1214)
... 19 common frames omitted

但是,如果我只剩下几个证书来修剪信任文件,则不会发生错误。

这是一个JDK错误吗?我怎样才能避免它?

JSSE 没有给出更清晰的警报可能是一个错误,但存在一个根本问题。

当 SSL/TLS 服务器请求客户端身份验证时,它通常会在证书请求消息中发送客户端应使用的证书颁发机构列表,请参阅 rfc5246 7.4.4 或更早版本。由于您信任大量的自签名证书,其中每个自签名者有效地充当自己的 CA,这意味着您的服务器需要发送大量的 CA 列表- 但此列表总共限制为 65535 字节。您的异常显示您正在尝试发送不适合 65535 字节106142 字节;这意味着您的证书名称(主题)平均约为 150 字节,如果这些名称完全在您的企业中使用,因此可能不需要像公共网络这样的全局唯一名称(尤其是具有增强身份要求的 EV),在我看来这似乎有点偏高。

如果所有客户端都知道要使用哪个证书而没有出现提示,则一种可能的解决方法是让服务器将 CA 列表作为空发送,这是允许的,但不鼓励这样做。JSSE 只是从信任管理器的getAcceptedIssuers()方法填充 CertReq.CAlist,而 TrustManager API 是为自定义而设计的,因此您只需将真正的 X509TrustManager 包装为正常验证收到的证书链,但getAcceptedIssuers()作为空数组返回。这对于实际的Java类(SSLContextet amici)来说相当容易,但我不确定在Netty的"改进"中究竟在哪里寻找。

但是,正如 EJP 的评论中所指出的,更好的解决方案不是单独信任大量自签名证书,而是让 CA 颁发客户端证书,然后服务器只需要信任该 CA(以及它颁发的可传递证书),CertReq 自动仅指定该 CA。如果您还没有合适的已建立 CA 可供使用,则有许多选项可以执行自己的 CA,在此处的其他 Q 和其他堆栈中讨论(IME 主要是安全性。SX 统一。SX 和 serverfault),但鉴于您使用的是 Java,请记住,因为 j7keytool -gencert执行最小但可用的 CA 函数。(除了密钥对和CSR的产生之外,keytool已经回到了黑暗时代。

最新更新