使用 Apache HttpClient 4.2.1。使用从基于表单的登录示例中复制的代码
http://hc.apache.org/httpcomponents-client-ga/examples.html
访问受 SSL 保护的登录表单时出现异常:
Getting library items from https://appserver.gtportalbase.com/agileBase/AppController.servlet?return=blank
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
Closing http connection
at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:572)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)
at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:294)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:640)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:479)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:784)
据我所知,证书很好(请参阅堆栈跟踪之前的 URL),没有过期 - 浏览器不会提示。
我尝试将证书导入我的 keystore
How to handle invalid SSL certificates with Apache HttpClient?
没有变化。我相信您可以创建一个自定义 SSLContext 来强制 Java 忽略该错误,但我宁愿修复根本原因,因为我不想打开任何安全漏洞。
有任何想法吗?
最佳答案
编辑 我意识到这个答案很久以前就被接受了,并且也被投票了 3 次,但它(至少部分)是不正确的,所以这里有更多关于这个异常(exception)的信息。造成的不便,深表歉意。
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
这通常是远程服务器根本没有发送证书时引发的异常。但是,使用 Apache HTTP Client 时会遇到一个极端情况,因为它在这个版本中的实现方式,以及因为
sun.security. ssl.SSLSocketImpl.getSession()
的方式。被实现。使用 Apache HTTP 客户端时,远程证书不受信任时也会抛出此异常,通常会抛出“
sun.security.validator.ValidatorException: PKIX path building failed
”。发生这种情况的原因是因为 Apache HTTP 客户端试图获取
SSLSession
和对等证书,然后再做任何其他事情。提醒一下,有3 ways of initiating the handshake with an
SSLSocket
:
- calling startHandshake which explicitly begins handshakes, or
- any attempt to read or write application data on this socket causes an implicit handshake, or
- a call to getSession tries to set up a session if there is no currently valid session, and an implicit handshake is done.
以下是 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
.这是 Apache HTTP Client's
AbstractVerifier
中实现的逻辑由其 org.apache.http.conn.ssl.SSLSocketFactory
使用在 4.2.1 版中。更高版本 make an explicit call to startHandshake()
,基于 issue HTTPCLIENT-1346 中的报告.这最终似乎来自
sun.security. ssl.SSLSocketImpl.getSession()
的实现, 捕获潜力 IOException
调用 startHandshake(false)
时抛出 s (内部方法),无需进一步抛出。这可能是一个错误,尽管这不会对安全产生重大影响,因为 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
”。 (通过在没有获得对等证书的情况下获得 session 没有漏洞)。如何解决这个问题
与不受信任的证书的任何其他问题一样,问题在于确保您使用的信任存储包含必要的信任 anchor (即颁发您要验证的链的 CA 证书,或者可能是实际的服务器特殊情况的证明)。
要解决此问题,您应该将 CA 证书(或者可能是服务器证书本身)导入您的信任库。你可以这样做:
cacerts
文件(这不一定是最好的,因为这会影响使用该 JRE 的所有应用程序),-Djavax.net.ssl.trustStore=...
选项进行配置),SSLContext
用于该连接(如 this answer 中所述)。 (有些人建议使用什么都不做的信任管理器,但这会使您的连接容易受到 MITM 攻击。)初步答案
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。
同时,解决此问题的一种方法是创建自己的
org.apache.http.conn.ssl.SSLSocketFactory
。并将其用于与 Apache Http 客户端的连接。该工厂需要创建一个
SSLSocket
像往常一样,使用 sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
在返回该套接字之前,禁用 TLS,否则默认情况下会启用。
关于java - Apache HTTPClient SSLPeerUnverifiedException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23710504/