Netty SSL 处理程序在 badssl.com 上错误地失败
Netty SSL handler fails incorrectly on badssl.com
我正在开发一个使用 Netty 来管理其 SSL/TLS 连接的应用程序。我正在尝试测试它是否正确处理各种证书错误,为此我使用 badssl.com.
但是,当我尝试连接到 badssl.com 时,出现异常(为简洁明了,已编辑并删除了完整的调用堆栈跟踪):
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: javax.net.ssl.SSLHandshakeException: Handshake failed
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:442)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:248)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
...
Caused by: javax.net.ssl.SSLHandshakeException: Handshake failed
at com.android.org.conscrypt.OpenSSLEngineImpl.unwrap(OpenSSLEngineImpl.java:436)
at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:1006)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1094)
...
Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:318)
at com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:219)
...
Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:318)
at com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:219)
at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:113)
以下 guide for Android developers links Trust anchor for certification path not found.
异常,具有未识别的 CA、自签名证书或缺少中间 CA。但是 badssl.com 不是这种情况 - phone 的浏览器可以识别证书,我什至可以使用 HTTPSUrlConnection 成功地联系它。
当我使用 InsecureTrustManagerFactory.INSTANCE 初始化我的安全时,Netty 连接成功,但这不是一个长期的解决方案。
以下是相关的代码片段:
初始化 SslContext
sslContext = SslContextBuilder
.forClient()
.sslProvider(SslProvider.JDK)
// TODO p0: Ensure all the versions we support have this algorithm installed
//.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
添加 SSL 处理程序:
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(sslContext.newHandler(ch.alloc(), host.getHostName(), host.getPort()));
认证后写入HTTP请求:
ChannelFuture future = bootstrap.connect(host).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
String request = "GET https://badssl.com/\n" +
"Host: +" + host.getHostName() + "\n" +
"Accept: text/html,application/xhtml+xml,application/xml;"
+ "DNT: 1\n\n";
future.channel().writeAndFlush(Unpooled.copiedBuffer(
request, Charset.forName("ASCII")));
}
这是怎么回事?为什么 badssl.com 没有被识别?我还尝试了一些带有已知错误证书的域,在这些域中连接似乎错误地成功了。为 SSL 客户端给出的唯一示例是 SecureChatClient,它使用 InsecureTrustManager。
我使用的是 Netty 4.1,我的测试 phone 是带有 Android 5.0 的 LG G3。
我猜你的代码没有使用 Server Name Indication (SNI)。在这种情况下,与 badssl.com 的 TLS 握手将生成证书:
Subject: ...CN=badssl-fallback-unknown-subdomain-or-no-sni
Issuer: ...CN=BadSSL Intermediate Certificate Authority
此证书由 BadSSL 本身颁发,代码正确地抱怨未找到证书路径的信任锚,因为此证书未由受信任的 CA 签名。
.. I can even use HTTPSUrlConnection successfully ..
HTTPSUrlConnection 使用 SNI,这时服务器会发送一个由受信任的 CA 签名的不同证书:
Subject: ... CN=*.badssl.com
Issuer: ... CN=COMODO RSA Domain Validation Secure Server CA
不幸的是,关于如何在客户端使用 SNI 的文档似乎很少见,但在更高版本的 Netty 中,缺少支持似乎是一个问题others noticed too and maybe there is fix。
我正在开发一个使用 Netty 来管理其 SSL/TLS 连接的应用程序。我正在尝试测试它是否正确处理各种证书错误,为此我使用 badssl.com.
但是,当我尝试连接到 badssl.com 时,出现异常(为简洁明了,已编辑并删除了完整的调用堆栈跟踪):
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: javax.net.ssl.SSLHandshakeException: Handshake failed
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:442)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:248)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
...
Caused by: javax.net.ssl.SSLHandshakeException: Handshake failed
at com.android.org.conscrypt.OpenSSLEngineImpl.unwrap(OpenSSLEngineImpl.java:436)
at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:1006)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1094)
...
Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:318)
at com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:219)
...
Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:318)
at com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:219)
at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:113)
以下 guide for Android developers links Trust anchor for certification path not found.
异常,具有未识别的 CA、自签名证书或缺少中间 CA。但是 badssl.com 不是这种情况 - phone 的浏览器可以识别证书,我什至可以使用 HTTPSUrlConnection 成功地联系它。
当我使用 InsecureTrustManagerFactory.INSTANCE 初始化我的安全时,Netty 连接成功,但这不是一个长期的解决方案。
以下是相关的代码片段: 初始化 SslContext
sslContext = SslContextBuilder
.forClient()
.sslProvider(SslProvider.JDK)
// TODO p0: Ensure all the versions we support have this algorithm installed
//.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
添加 SSL 处理程序:
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(sslContext.newHandler(ch.alloc(), host.getHostName(), host.getPort()));
认证后写入HTTP请求:
ChannelFuture future = bootstrap.connect(host).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
String request = "GET https://badssl.com/\n" +
"Host: +" + host.getHostName() + "\n" +
"Accept: text/html,application/xhtml+xml,application/xml;"
+ "DNT: 1\n\n";
future.channel().writeAndFlush(Unpooled.copiedBuffer(
request, Charset.forName("ASCII")));
}
这是怎么回事?为什么 badssl.com 没有被识别?我还尝试了一些带有已知错误证书的域,在这些域中连接似乎错误地成功了。为 SSL 客户端给出的唯一示例是 SecureChatClient,它使用 InsecureTrustManager。
我使用的是 Netty 4.1,我的测试 phone 是带有 Android 5.0 的 LG G3。
我猜你的代码没有使用 Server Name Indication (SNI)。在这种情况下,与 badssl.com 的 TLS 握手将生成证书:
Subject: ...CN=badssl-fallback-unknown-subdomain-or-no-sni
Issuer: ...CN=BadSSL Intermediate Certificate Authority
此证书由 BadSSL 本身颁发,代码正确地抱怨未找到证书路径的信任锚,因为此证书未由受信任的 CA 签名。
.. I can even use HTTPSUrlConnection successfully ..
HTTPSUrlConnection 使用 SNI,这时服务器会发送一个由受信任的 CA 签名的不同证书:
Subject: ... CN=*.badssl.com
Issuer: ... CN=COMODO RSA Domain Validation Secure Server CA
不幸的是,关于如何在客户端使用 SNI 的文档似乎很少见,但在更高版本的 Netty 中,缺少支持似乎是一个问题others noticed too and maybe there is fix。