Apache HTTPClient SSLPeerUnverifiedException

Oli*_*hll 14 java ssl apache-httpclient-4.x

使用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)
Run Code Online (Sandbox Code Playgroud)

据我所知,证书很好(参见堆栈跟踪之前的URL),没有过期 - 浏览器不会抱怨.

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

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

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

有任何想法吗?

Bru*_*uno 21

编辑我意识到这个答案很久以前被接受了,并且已经被投票了3次,但它(至少部分)是不正确的,所以这里有更多关于这个例外.不便之处敬请原谅.

javax.net.ssl.SSLPeerUnverifiedException:peer未经过身份验证

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

使用Apache HTTP Client时,如果远程证书不受信任,也会抛出此异常,这通常会抛出" sun.security.validator.ValidatorException: PKIX path building failed".

发生这种情况的原因是Apache HTTP Client SSLSession在执行任何其他操作之前尝试获取对等证书.

提醒一下,有三种方式可以启动握手SSLSocket:

  • 调用startHandshake明确地开始握手,或者
  • 任何在此套接字上读取或写入应用程序数据的尝试都会导致隐式握手,或者
  • 如果没有当前有效的会话,则调用getSession会尝试建立会话,并完成隐式握手.

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

例1:

    SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
    SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example",
            443);
    sslSocket.startHandshake();
Run Code Online (Sandbox Code Playgroud)

抛出" 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();
Run Code Online (Sandbox Code Playgroud)

这也会引发" 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();
Run Code Online (Sandbox Code Playgroud)

但是,这会抛出javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated.

这是在版本4.2.1中使用的Apache HTTP Client中AbstractVerifier实现的逻辑org.apache.http.conn.ssl.SSLSocketFactory.根据问题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();
Run Code Online (Sandbox Code Playgroud)

值得庆幸的是,javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed每当你真正尝试使用它时,它仍然会抛出" " SSLSocket(没有获得对等证书就没有获得会话的漏洞).


如何解决这个问题

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

要解决此问题,您应该将CA证书(或可能是服务器证书本身)导入信任库.你可以这样做:

  • 在您的JRE信任库中,通常是cacerts文件(不一定是最好的,因为这会影响使用该JRE的所有应用程序),
  • 在您的信任库的本地副本中(您可以使用-Djavax.net.ssl.trustStore=...选项进行配置),
  • 通过SSLContext为该连接创建特定的(如本答案中所述).(有些人建议使用不执行任何操作的信任管理器,但这会使您的连接容易受到MITM攻击.)

初步答复

javax.net.ssl.SSLPeerUnverifiedException:peer未经过身份验证

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

此服务器明显未配置为正确支持TLS.这会失败(您将无法获得远程证书):

openssl s_client -tls1 -showcerts -connect appserver.gtportalbase.com:443
Run Code Online (Sandbox Code Playgroud)

但是,SSLv3似乎有效:

openssl s_client -ssl3 -showcerts -connect appserver.gtportalbase.com:443
Run Code Online (Sandbox Code Playgroud)

如果你知道谁在运行这个服务器,那么值得联系他们来解决这个问题.至少现在服务器应该真的支持TLSv1.

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

这个工厂需要SSLSocket像往常一样sslSocket.setEnabledProtocols(new String[] {"SSLv3"});在返回该套接字之前使用它来禁用TLS,否则默认情况下会启用TLS.

  • @OliverKohll该信任管理器只是实现默认行为,即在JRE中使用信任存储.如果要使用其他信任库,只需设置-Djavax.net.ssl.trustStore = xxx.您不需要代码. (2认同)