与Proxy的HTTPS连接导致SSLHandshakeException

时间:2014-02-26 14:24:00

标签: java ssl proxy

我已经在这个问题上苦苦挣扎了好几天了,我似乎无法找到有用的答案......

每次我想将它与代理服务器一起使用时,我有一些软件会导致SSLHandshakeExceptions。我已经能够使用以下代码复制该问题(这比实际应用程序简单):

    HttpsURLConnection con = (HttpsURLConnection) new URL("https://www.google.com").openConnection();
    try {
        con.connect();
    } catch(Exception e) {
        e.printStackTrace();
    }

如果我运行此代码连接到WiFi热点(不需要代理服务器),我会得到连接,一切都按预期工作:

java -cp . SampleJavaURLTest

但是,如果我使用代理服务器运行相同的Java命令:

java -cp . -DproxySet=true -DproxyHost={myProxyHost} -DproxyPort={myProxyPort} SampleJavaURLTest

我收到此异常(使用代码片段中的e.printStackTrace()打印,以使行号无关:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1731)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:241)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:235)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1206)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:136)
at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:593)
at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:529)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:925)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1170)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1197)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1181)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133)
at SampleJavaURLTest.main(SampleJavaURLTest.java:116)
Caused 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:323)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:217)
at sun.security.validator.Validator.validate(Validator.java:218)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:249)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1185)
... 11 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:174)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:318)
... 17 more

因此,第一个逻辑建议是“使用InstallCert程序并添加google的证书”。然而,这不是问题,不知道Java是不是喜欢通过代理服务器处理证书的方式。

我正在使用完全相同的JVM运行两个示例,除了命令行中的代理设置之外,没有进一步的配置更改。事实上,两个测试都使用相同的控制台窗口完成,两个测试之间只需几秒钟来切换网络...这是一个java -v打印输出:

java version "1.6.0_30"
Java(TM) SE Runtime Environment (build 1.6.0_30-b12)
Java HotSpot(TM) Client VM (build 20.5-b03, mixed mode, sharing)

而且,为了100%肯定,我还测试了一个更新的JVM:

java version "1.7.0_10"
Java(TM) SE Runtime Environment (build 1.7.0_10-b18)
Java HotSpot(TM) 64-Bit Server VM (build 23.6-b04, mixed mode)

作为解决方法,我使用此方法禁用了所有证书验证:

http://code.google.com/p/misc-utils/wiki/JavaHttpsUrl

虽然它并没有真正影响我,但我可以忍受这种解决方法(如果有人在使用虚假证书进行通信时不那么重要),我真的很想知道如何正确地获取证书,因为他们应该。我错过了什么?

1 个答案:

答案 0 :(得分:6)

如果我理解你,你的SSL连接没有代理成功,如果你禁用证书验证也会成功使用代理,但如果启用证书验证则会失败。在这种情况下,代理正在篡改SSL,例如它会进行中间人攻击(在某些防火墙中也称为SSL拦截或在squid中称为SSL)。如果这不是攻击,而是此代理的已知功能(例如,扫描SSL连接以查找病毒等),则需要导入代理拦截CA,因为由于中间人你不再说话SSL到Web服务器但是代理和代理然后将SSL与服务器进行对话。