编辑我意识到这个答案很久以前就被接受了,并且也被提高了3次,但它(至少部分)是不正确的,所以这里有更多关于这个例外的信息。对于给您带来的不便,我们深表歉意。
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
这通常是当远程服务器根本没有发送证书时引发的异常。但是,有一个边缘情况,在使用Apache HTTP客户端时会遇到这种情况,因为它在此版本中的实现方式以及实现方式。sun.security. ssl.SSLSocketImpl.getSession()
使用Apache HTTP客户端时,当远程证书不受信任时,也会引发此异常,这更常引发“”。sun.security.validator.ValidatorException: PKIX path building failed
发生这种情况的原因是因为Apache HTTP客户端在执行任何其他操作之前尝试获取和对等证书。SSLSession
提醒一下,有 3 种方法可以使用 SSLSocket
启动握手:
- 调用开始显式开始握手的手摇,或者
- 任何在此套接字上读取或写入应用程序数据的尝试都会导致隐式握手,或者
- 如果当前没有有效的会话,则对 getSession 的调用会尝试设置会话,并且隐式握手已完成。
这里有3个例子,所有的例子都针对一个具有不受信任的证书的主机(使用,而不是Apache)。javax.net.ssl.SSLSocketFactory
示例 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
这是Apache HTTP Client的AbstractVerifier
中实现的逻辑,由其org.apache.http.conn.ssl.SSLSocketFactory
在4.2.1版中使用。更高版本根据问题 HTTPCLIENT-1346 中的报告显式调用 startHandshake()。
这最终似乎来自 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 信任存储中,通常是文件(这不一定是最好的,因为这会影响使用该 JRE 的所有应用程序),
cacerts
- 在信任库的本地副本中(可以使用选项进行配置),
-Djavax.net.ssl.trustStore=...
- 通过为该连接创建特定连接(如本答案中所述)。(有些人建议使用什么都不做的信任管理器,但这会使你的连接容易受到MITM攻击。
SSLContext
初步答案
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
这与信任证书无关,或者您必须创建自定义 :这是由于服务器根本没有发送任何证书。SSLContext
此服务器显然未配置为正确支持 TLS。此操作将失败(您不会获得远程证书):
openssl s_client -tls1 -showcerts -connect appserver.gtportalbase.com:443
但是,SSLv3 似乎可以工作:
openssl s_client -ssl3 -showcerts -connect appserver.gtportalbase.com:443
如果您知道谁在运行此服务器,则值得与他们联系以解决此问题。至少在现在,服务器应该真正支持TLSv1。
同时,解决此问题的一种方法是创建自己的方法,并将其用于与Apache Http客户端的此连接。org.apache.http.conn.ssl.SSLSocketFactory
此工厂需要像往常一样创建一个在返回该套接字之前使用的 TLS,以禁用 TLS,否则默认情况下将启用 TLS。SSLSocket
sslSocket.setEnabledProtocols(new String[] {"SSLv3"});