简体   繁体   English

Apache HTTPClient SSLPeerUnverifiedException

[英]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: 你可以这样做:

  • in your JRE trust store, usually the cacerts file (that's not necessarily the best, because that would affect all applications using that JRE), 在您的JRE信任库中,通常是cacerts文件(不一定是最好的文件,因为这会影响使用该JRE的所有应用程序),
  • in a local copy of your trust store (which you can configure using the -Djavax.net.ssl.trustStore=... options), 在您的信任库的本地副本中(可以使用-Djavax.net.ssl.trustStore=...选项进行配置),
  • by creating a specific 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.) (有人建议使用不执行任何操作的信任管理器,但这会使您的连接容易受到MITM攻击。)

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.

相关问题 带有httpClient的SSLPeerUnverifiedException - SSLPeerUnverifiedException with httpClient Apache HttpClient错误:javax.net.ssl.SSLPeerUnverifiedException:对等方未通过身份验证 - Apache HttpClient Error: javax.net.ssl.SSLPeerUnverifiedException: Peer Not Authenticated javax.net.ssl.SSLPeerUnverifiedException:未在Tomcat中使用Apache HttpClient对等体进行身份验证 - javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated in Tomcat with Apache HttpClient Apache Commons AsyncClient-忽略证书-SSLPeerUnverifiedException - Apache Commons AsyncClient - Ignore Certificates - SSLPeerUnverifiedException 使用 HttpClient POST 到表单但获得 SSLPeerUnverifiedException 但浏览器工作正常 - Using HttpClient to POST to Form but Getting SSLPeerUnverifiedException yet browser Works Fine HttpClient javax.net.ssl.SSLPeerUnverifiedException:peer在时移时未经过身份验证? - HttpClient javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated when time shifted? Apache HttpClient 摘要身份验证 - Apache HttpClient Digest authentication Apache HttpClient ResponseHandler异常 - Apache HttpClient ResponseHandler Exceptions Android-Apache Mime和httpClient - Android - Apache Mime & httpClient Apache HttpClient和自定义端口 - Apache HttpClient and custom ports
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM