编辑我意识到这个答案前接受了很长时间,也已经upvoted的3倍,但它是(至少部分)不正确,所以这里是一个比较有关此异常。不便之处敬请原谅。
javax.net.ssl.SSLPeerUnverifiedException:同行不认证
这是通常当远程服务器没有证书在所有抛出的异常。但是,由于在此版本中实施的方式以及实施sun.security. ssl.SSLSocketImpl.getSession()
的方式,在使用Apache HTTP Client时遇到了一个边缘情况。
当使用Apache HTTP Client时,当远程证书不可信时,这个异常也会被抛出,而这往往会抛出“sun.security.validator.ValidatorException: PKIX path building failed
”。
发生这种情况的原因是因为Apache HTTP客户端在做其他事情之前尝试获取SSLSession
和对等证书。
正如一个提醒,有3 ways of initiating the handshake with an SSLSocket
:
- 调用startHandshake其中明确开始握手,或
- 任何尝试读取或写入应用数据在此插座会导致一个隐含的握手,或
- 如果没有当前有效的会话并且隐式握手已完成,则调用getSession会尝试设置会话。
这里有3个例子中,所有针对与一个证书的主机是不信任(使用javax.net.ssl.SSLSocketFactory
,而不是Apache之一)。
实施例1:
SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example",
443);
sslSocket.startHandshake();
这抛出 “javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
”(如预期)。
实施例2:
SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example",
443);
sslSocket.getInputStream().read();
这也引发 “javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
”(如预期)。
实施例3:
SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example",
443);
SSLSession sslSession = sslSocket.getSession();
sslSession.getPeerCertificates();
然而,这将引发javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
。
这是在版本4.2.1中由其org.apache.http.conn.ssl.SSLSocketFactory
使用的Apache HTTP Client's AbstractVerifier
中实施的逻辑。后续版本make an explicit call to startHandshake()
,基于issue HTTPCLIENT-1346中的报告。
这最终似乎来自的sun.security. ssl.SSLSocketImpl.getSession()
实施,其中捕获电位IOException
在叫startHandshake(false)
(内部方法)时抛出,而无需进一步抛它。这可能是一个错误,虽然这不应该有一个巨大的安全影响,因为SSLSocket
仍然会被关闭。
例4:
SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example",
443);
SSLSession sslSession = sslSocket.getSession();
// sslSession.getPeerCertificates();
sslSocket.getInputStream().read();
值得庆幸的是,这仍然会抛出“javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
”,只要你真正尝试(通过获取会话没有得到同行的证书有没有漏洞)使用SSLSocket
。
如何解决这个
像使用证书的任何其他问题不受信任的,这是确保你正在使用的信任存储包含必要的信任锚的问题(即CA颁发您试图验证的链的证书,或者特殊情况下的实际服务器证书)。
要解决此问题,您应该将CA证书(或可能是服务器证书本身)导入您的信任存储区。在您的信任存储的本地副本
- 在JRE信任库,通常是
cacerts
文件(这并不一定是最好的,因为这将使用该JRE会影响所有的应用程序),
- :你可以这样做(您可以使用
-Djavax.net.ssl.trustStore=...
选项进行配置),
- 通过为该连接创建特定
SSLContext
(如this answer中所述)。 (有人建议用它来,什么也不做的信托经理,但这样会使您的连接容易受到中间人攻击。)
初始答案
javax.net.ssl中。SSLPeerUnverifiedException:同行不认证
这有什么做与信任的证书或您不必创建一个自定义SSLContext
:这是由于服务器不发送任何证书都没有。
该服务器明显未配置为正确支持TLS。这种失败(您将无法获得远程证书):
openssl s_client -tls1 -showcerts -connect appserver.gtportalbase.com:443
然而,在SSLv3似乎工作:
openssl s_client -ssl3 -showcerts -connect appserver.gtportalbase.com:443
如果您知道是谁在运行这个服务器,值得与他们联系解决这个问题。至少现在服务器应该至少支持TLSv1。
同时,解决此问题的一种方法是创建您自己的org.apache.http.conn.ssl.SSLSocketFactory
并将其用于与Apache Http客户端的此连接。
这个工厂需要像往常一样创建SSLSocket
,在返回该套接字之前使用sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
来禁用TLS,否则默认情况下会启用TLS。
嗯,我得到了两个远程证书。将尝试从几个不同的客户端 –
从OSX,这两个工作,从Linux,只有ssl3。将调查服务器配置。 –
这可能与密码套件有关。在你的OSX客户端上,'openssl ciphers DEFAULT'与你的Linux客户端有相同的密码套件吗?一些会在OSX列表中,但不在Linux上? – Bruno