[英]Apache HTTPClient SSLPeerUnverifiedException
Using Apache HttpClient 4.2.1. 使用Apache HttpClient 4.2.1。 Using code copied from the form based login example
使用从基于表单的登录示例复制的代码
http://hc.apache.org/httpcomponents-client-ga/examples.html http://hc.apache.org/httpcomponents-client-ga/examples.html
I get an exception when accessing an SSL protected login form: 访问受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)
The certificate as far as I can tell is fine (see the URL before the stack trace), not expired - browsers don't complain. 据我所知,该证书很好(请参阅堆栈跟踪之前的URL),并且不会过期-浏览器不会抱怨。
I've tried importing the certificate into my keystore a la 我尝试将证书导入到我的密钥库中
How to handle invalid SSL certificates with Apache HttpClient? 如何使用Apache HttpClient处理无效的SSL证书?
with no change. 没有变化。 I believe you can create a custom SSLContext to force Java to ignore the error but I'd rather fix the root cause as I don't want to open up any security holes.
我相信您可以创建一个自定义SSLContext来强制Java忽略该错误,但是我宁愿解决根本原因,因为我不想打开任何安全漏洞。
Any ideas? 有任何想法吗?
EDIT I realise this answer was accepted a long time ago and has also been upvoted 3 times, but it was (at least partly) incorrect, so here is a bit more about this exception. 编辑我知道这个答案很久以前就已经被接受,并且也被投票了3次,但这是(至少部分是)错误的,所以这里有更多关于此异常的信息。 Apologies for the inconvenience.
不便之处敬请原谅。
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
javax.net.ssl.SSLPeerUnverifiedException:对等方未通过身份验证
This is usually an exception thrown when the remote server didn't send a certificate at all. 当远程服务器根本不发送证书时, 通常会抛出此异常。 However, there is an edge case, which is encountered when using Apache HTTP Client, because of the way it was implemented in this version, and because of the way
sun.security. ssl.SSLSocketImpl.getSession()
但是,由于在此版本中实现的方式以及
sun.security. ssl.SSLSocketImpl.getSession()
的方式,在使用Apache HTTP Client时会遇到一些sun.security. ssl.SSLSocketImpl.getSession()
sun.security. ssl.SSLSocketImpl.getSession()
is implemented. sun.security. ssl.SSLSocketImpl.getSession()
已实现。
When using Apache HTTP Client, this exception will also be thrown when the remote certificate isn't trusted, which would more often throw " sun.security.validator.ValidatorException: PKIX path building failed
". 使用Apache HTTP Client时,当不信任远程证书时也会引发此异常,该异常通常会引发“
sun.security.validator.ValidatorException: PKIX path building failed
”。
The reasons this happens is because Apache HTTP Client tries to get the SSLSession
and the peer certificate before doing anything else. 发生这种情况的原因是因为Apache HTTP Client在执行其他操作之前会尝试获取
SSLSession
和对等证书。
Just as a reminder, there are 3 ways of initiating the handshake with an SSLSocket
: 提醒一下,使用
SSLSocket
发起握手的方法有3种 :
- calling startHandshake which explicitly begins handshakes, or
调用开始显式开始握手的startHandshake,或
- 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.
如果当前没有有效的会话,则对getSession的调用尝试建立会话,并且隐式握手已完成。
Here are 3 examples, all against a host with a certificate that isn't trusted (using javax.net.ssl.SSLSocketFactory
, not the Apache one). 这是3个示例,所有示例均针对具有不可信证书的主机(使用
javax.net.ssl.SSLSocketFactory
,而不是Apache)。
Example 1: 范例1:
SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example",
443);
sslSocket.startHandshake();
This throws " javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
" (as expected). 这将引发“
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
”(按预期方式)。
Example 2: 范例2:
SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example",
443);
sslSocket.getInputStream().read();
This also throws " javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
" (as expected). 这也会引发“
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
”(按预期方式)。
Example 3: 范例3:
SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example",
443);
SSLSession sslSession = sslSocket.getSession();
sslSession.getPeerCertificates();
This, however, throws javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
. 但是,这会引发
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
。
This is the logic implemented in Apache HTTP Client's AbstractVerifier
used by its org.apache.http.conn.ssl.SSLSocketFactory
in version 4.2.1. 这是在Apache HTTP客户端的 4.2.1版的
org.apache.http.conn.ssl.SSLSocketFactory
使用的AbstractVerifier
实现的逻辑。 Later versions make an explicit call to startHandshake()
, based on reports in issue HTTPCLIENT-1346 . 更高版本基于问题HTTPCLIENT-1346 显式调用
startHandshake()
。
This ultimately seems to come from the implementation of sun.security. ssl.SSLSocketImpl.getSession()
最终,这似乎来自
sun.security. ssl.SSLSocketImpl.getSession()
的实现sun.security. ssl.SSLSocketImpl.getSession()
sun.security. ssl.SSLSocketImpl.getSession()
, which catches potential IOException
s thrown when calling startHandshake(false)
(internal method), without throwing it further. sun.security. ssl.SSLSocketImpl.getSession()
,它捕获调用startHandshake(false)
(内部方法)时引发的潜在IOException
,而不会进一步抛出该异常。 This might be a bug, although this shouldn't have a massive security impact, since the SSLSocket
will still be closed anyway. 这可能是一个错误,尽管这不会对安全产生重大影响,因为
SSLSocket
仍将关闭。
Example 4: 范例4:
SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example",
443);
SSLSession sslSession = sslSocket.getSession();
// sslSession.getPeerCertificates();
sslSocket.getInputStream().read();
Thankfully, this will still throw " javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
", whenever you actually try to use that SSLSocket
(no loophole there by getting the session without getting the peer certificate). 值得庆幸的是,每当您实际尝试使用该
SSLSocket
时,仍然会抛出“ javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
”(在没有获取对等证书的情况下获取会话不会造成漏洞) )。
How to fix this 如何解决这个问题
Like any other issue with certificates that are not trusted, it's a matter of making sure the trust store you're using contains the necessary trust anchors (ie the CA certificates that issued the chain you're trying to verify, or possibly the actual server certificate for exceptional cases). 像其他任何不信任的证书问题一样,确保您使用的信任库包含必要的信任锚(例如,颁发要尝试验证的链的CA证书,或者可能是实际服务器)特殊情况证书)。
To fix this, you should import the CA certificate (or possibly the server certificate itself) into your trust store. 要解决此问题,您应该将CA证书(或者可能是服务器证书本身)导入到信任库中。 You can do this:
你可以这样做:
cacerts
file (that's not necessarily the best, because that would affect all applications using that JRE), cacerts
文件(不一定是最好的文件,因为这会影响使用该JRE的所有应用程序), -Djavax.net.ssl.trustStore=...
options), -Djavax.net.ssl.trustStore=...
选项进行配置), SSLContext
for that connection (as described in this answer ). SSLContext
(如本答案中所述 )。 (Some suggest to use a trust manager that does nothing, but this would make your connection vulnerable to MITM attacks.) Initial answer 初步答案
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
javax.net.ssl.SSLPeerUnverifiedException:对等方未通过身份验证
This has nothing to do with trusting certificates or you having to create a custom SSLContext
: this is due to the fact that the server isn't sending any certificate at all. 这与信任证书无关,或者您必须创建自定义
SSLContext
:这是由于服务器根本不发送任何证书。
This server is visibly not configured to support TLS properly. 显然,该服务器未配置为正确支持TLS。 This fails (you won't get a remote certificate):
这将失败(您将不会获得远程证书):
openssl s_client -tls1 -showcerts -connect appserver.gtportalbase.com:443
However, SSLv3 seems to work: 但是,SSLv3似乎可以工作:
openssl s_client -ssl3 -showcerts -connect appserver.gtportalbase.com:443
If you know who's running this server, it would be worth contacting them to fix this problem. 如果您知道谁在运行此服务器,则值得与他们联系以解决此问题。 Servers should really support TLSv1 at least nowadays.
至少现在,服务器应该真正支持TLSv1。
Meanwhile, one way to fix this problem would be to create your own org.apache.http.conn.ssl.SSLSocketFactory
and use it for this connection with Apache Http client. 同时,解决此问题的一种方法是创建您自己的
org.apache.http.conn.ssl.SSLSocketFactory
并将其用于与Apache Http客户端的此连接。
This factory would need to create an SSLSocket
as usual, use sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
该工厂将需要照常创建一个
SSLSocket
,请使用sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
before returning that socket, to disable TLS, which would otherwise be enabled by default. 返回该套接字之前,请禁用TLS,否则默认情况下会启用该功能。
Create a custom context so you can log why the cert is invalid. 创建一个自定义上下文,以便您可以记录证书无效的原因。 Or just debug into it.
或者只是调试它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.