Netty 4客户端SSL配置以请求所有HTTPS站点

11
我有一个基于Netty(4.1b7)的网络爬虫,大量请求不同的网站,包括http和https网站,并且我正在尝试配置Netty客户端以处理具有不同身份验证设置的https站点。
当我没有自己的证书时,我使用简单的Netty配置:
 SslContext sslCtx = SslContextBuilder.forClient().build();

            SSLEngine sslEngine = sslCtx.newEngine(ch.alloc(), host, port);
            p.addLast("ssl", new SslHandler(sslEngine));

大约一半的https网站请求成功,但其他网站则失败了,原因如下:

Caused by: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1728)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:304)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1506)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
at sun.security.ssl.Handshaker$1.run(Handshaker.java:919)
at sun.security.ssl.Handshaker$1.run(Handshaker.java:916)
at java.security.AccessController.doPrivileged(Native Method)
at sun.security.ssl.Handshaker$DelegatedTask.run(Handshaker.java:1369)
at io.netty.handler.ssl.SslHandler.runDelegatedTasks(SslHandler.java:1164)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1067)
... 19 moreCaused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:281)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:136)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1493)
... 27 more Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:146)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
... 33 more

或者:

Caused by: javax.net.ssl.SSLException: Received fatal alert: handshake_failure
at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1666)
at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1634)
at sun.security.ssl.SSLEngineImpl.recvAlert(SSLEngineImpl.java:1800)
at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:1083)
at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:907)
at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:781)
at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1138)

当我尝试生成本地证书并将其设置为:
System.setProperty("javax.net.ssl.trustStore", "/etc/ssl/my/cacerts.jks");
    System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
    System.setProperty("javax.net.ssl.keyStore", "/etc/ssl/my/keystore.jks");
    System.setProperty("javax.net.ssl.keyStorePassword", "changeit");

然后所有的HTTPS网站都出现了类似以下的错误:

Caused by: java.security.cert.CertificateException: found no certificates: /etc/ssl/my/cacerts.jks
at io.netty.handler.ssl.PemReader.readCertificates(PemReader.java:83) ~[netty-all-4.1.0.Beta7.jar:4.1.0.Beta7]
at io.netty.handler.ssl.SslContext.toX509Certificates(SslContext.java:967)
....
Caused by: java.security.KeyException: found no private key: /etc/ssl/my/keystore.jks
at io.netty.handler.ssl.PemReader.readPrivateKey(PemReader.java:99) ~[netty-all-4.1.0.Beta7.jar:4.1.0.Beta7]
at io.netty.handler.ssl.SslContext.toPrivateKey(SslContext.java:923)

我也尝试了来自 SO的建议,但还没有成功。

有什么问题或者有人能提供一份逐步指南来配置Netty 4+客户端以处理带有所有可能身份验证设置的https站点。

2个回答

7

最终我通过在代码中黑客TrustManager解决了问题(在SO上发现了这个技巧):

TrustManager[] trustAllCerts = new TrustManager[]{
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }

                public void checkClientTrusted(X509Certificate[] certs, String authType) {
                }

                public void checkServerTrusted(X509Certificate[] certs, String authType) {
                }
            }};

    // Ignore differences between given hostname and certificate hostname
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);

...
final SSLEngine sslEngine = sc.createSSLEngine(host, port);
            sslEngine.setUseClientMode(true);
            sslEngine.setNeedClientAuth(false);

            p.addLast("ssl", new SslHandler(sslEngine));

现在所有在浏览器中正常(至少Chrome不会显示警告)的https网站都由Netty读取。

1
我有一个类似的问题,需要使用客户端证书,相当于 curl --cacert 选项。在你的代码中,管道 phostport 是从哪里来的 - 没有这些,上面显示的代码就没有用处。 - Abhijit Sarkar

4

尝试按照以下方式构建sslCtx:

SslContext sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();

然后将其添加到管道中:

p.addLast(sslCtx.newHandler(ch.alloc()));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接