Apache HTTPClient SSLPeerUnverifiedException [英] Apache HTTPClient SSLPeerUnverifiedException

查看:107
本文介绍了Apache HTTPClient SSLPeerUnverifiedException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Apache HttpClient 4.2.1。使用从基于表单的登录示例复制的代码

Using Apache HttpClient 4.2.1. Using code copied from the form based login example

http://hc.apache.org/httpcomponents-client-ga/examples.html

我在访问时遇到异常受SSL保护的登录表单:

I get an exception when accessing an SSL protected login form:

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),未过期 - 浏览器不会抱怨。

The certificate as far as I can tell is fine (see the URL before the stack trace), not expired - browsers don't complain.

我已经尝试将证书导入我的密钥库了

I've tried importing the certificate into my keystore a la

如何使用Apache HttpClient处理无效的SSL证书?

没有任何变化。我相信你可以创建一个自定义SSLContext来强制Java忽略错误,但我宁愿修复根本原因,因为我不想打开任何安全漏洞。

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.

任何想法?

推荐答案

编辑我意识到这个答案很久以前就被接受了也被投票了3次,但它(至少部分)是不正确的,所以这里有更多关于这个例外。抱歉给您带来不便。

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. Apologies for the inconvenience.


javax.net.ssl.SSLPeerUnverifiedException:peer not authenticated

javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated

当远程服务器根本没有发送证书时,通常抛出异常。但是,有一个边缘情况,在使用Apache HTTP Client时会遇到,因为它在这个版本中的实现方式,以及 sun.security的方式。 ssl.SSLSocketImpl.getSession()已实现。

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() is implemented.

使用Apache HTTP Client时,如果远程证书不是,则也会抛出此异常t trusted,通常会抛出 sun.security.validator.ValidatorException:PKIX路径构建失败

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在执行任何其他操作之前尝试获取 SSLSession 和对等证书。

The reasons this happens is because Apache HTTP Client tries to get the SSLSession and the peer certificate before doing anything else.

提醒一下,有 3使用 SSLSocket 启动握手的方式:

Just as a reminder, there are 3 ways of initiating the handshake with an SSLSocket:



  • 调用startHandshake显式开始握手,或者

  • 任何在此套接字上读取或写入应用程序数据的尝试都会导致隐式握手,或

  • 如果当前没有,则调用getSession会尝试建立会话有效会话,并进行隐式握手。

以下是3个示例,全部针对主机使用不受信任的证书(使用 javax.net.ssl.SSLSocketFactory ,而不是Apache的证书)。

Here are 3 examples, all against a host with a certificate that isn't trusted (using javax.net.ssl.SSLSocketFactory, not the Apache one).

示例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路径构建失败(按预期方式)。

This throws "javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed" (as expected).

示例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路径构建失败(按预期方式)。

This also throws "javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed" (as expected).

示例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 not已验证的

这是 Apache HTTP Client的 AbstractVerifier 版本4.2.1中的.java#L570rel =nofollow noreferrer> org.apache.http.conn.ssl.SSLSocketFactory 。更高版本根据 startHandshake() / jira / browse / HTTPCLIENT-1346rel =nofollow noreferrer>问题HTTPCLIENT-1346 。

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. Later versions make an explicit call to startHandshake(), based on reports in issue HTTPCLIENT-1346.

这最终似乎来自< a href =https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/sun/security/ssl/SSLSocketImpl.java#L2182\"rel =nofollow noreferrer> sun.security。 ssl.SSLSocketImpl.getSession() ,它在捕获 startHandshake(false)时捕获潜在的 IOException (内部方法),不再进一步抛弃。这可能是一个错误,虽然这不会产生巨大的安全影响,因为 SSLSocket 仍然会被关闭。

This ultimately seems to come from the implementation of sun.security. ssl.SSLSocketImpl.getSession(), which catches potential IOExceptions thrown when calling startHandshake(false) (internal method), without throwing it further. This might be a bug, although this shouldn't have a massive security impact, since the SSLSocket will still be closed anyway.

示例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路径构建失败,每当你真正尝试使用 SSLSocket 时(通过获取会话没有漏洞)没有得到同行证书。)

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).

如何解决此问题

与任何其他不受信任的证书问题一样,这是确保您使用的信任存储包含必要的信任锚(即发布链的CA证书)的问题。尝试验证,或者可能是特殊情况下的实际服务器证书。)

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 (i.e. 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. You can do this:


  • 在您的JRE信任库中,通常是 cacerts 文件(这不一定是最好的,因为这会影响使用该JRE的所有应用程序),

  • 在您的信任库的本地副本中(您可以使用进行配置) -Djavax.net.ssl.trustStore = ... options),

  • 创建一个特定的 SSLContext 用于该连接(如此答案中所述)。 (有些人建议使用什么都不做的信任管理器,但这会使你的连接容易受到MITM攻击。)

  • in your JRE trust store, usually the cacerts file (that's not necessarily the best, because that would affect all applications using that JRE),
  • in a local copy of your trust store (which you can configure using the -Djavax.net.ssl.trustStore=... options),
  • by creating a specific SSLContext for that connection (as described in this answer). (Some suggest to use a trust manager that does nothing, but this would make your connection vulnerable to MITM attacks.)

初步答案


javax.net.ssl.SSLPeerUnverifiedException:peer not authenticated

javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated

这与信任证书无关,或者您必须创建自定义 SSLContext :这是因为服务器根本没有发送任何证书。

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.

该服务器明显没有配置为正确支持TLS。这失败了(你不会得到远程证书):

This server is visibly not configured to support TLS properly. This fails (you won't get a remote certificate):


openssl s_client -tls1 -showcerts -connect appserver.gtportalbase.com:443


然而,SSLv3似乎工作:

However, SSLv3 seems to work:


openssl s_client -ssl3 -showcerts -connect appserver.gtportalbase.com:443


如果您知道谁在运行此服务器,那么值得联系他们解决这个问题。服务器至少应该支持TLSv1。

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.

同时,解决这个问题的一种方法是创建自己的 org.apache.http。 conn.ssl.SSLSocketFactory 并将其用于与Apache Http客户端的此连接。

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.

此工厂需要创建 SSLSocket 像往常一样,在返回该套接字之前,使用 sslSocket.setEnabledProtocols(new String [] {SSLv3}); 来禁用TLS,否则将默认启用。

This factory would need to create an SSLSocket as usual, use sslSocket.setEnabledProtocols(new String[] {"SSLv3"}); before returning that socket, to disable TLS, which would otherwise be enabled by default.

这篇关于Apache HTTPClient SSLPeerUnverifiedException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆