2012-08-23 66 views
13

使用Apache HttpClient 4.2.1。使用从基于表单登录例如Apache HTTPClient SSLPeerUnverifiedException

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

我试图把该证书导入密钥库我一拉

How to handle invalid SSL certificates with Apache HttpClient?

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

任何想法?

回答

20

编辑我意识到这个答案前接受了很长时间,也已经upvoted的3倍,但它是(至少部分)不正确,所以这里是一个比较有关此异常。不便之处敬请原谅。

javax.net.ssl.SSLPeerUnverifiedException:同行不认证

这是通常当远程服务器没有证书在所有抛出的异常。但是,由于在此版本中实施的方式以及实施sun.security. ssl.SSLSocketImpl.getSession()的方式,在使用Apache HTTP Client时遇到了一个边缘情况。

当使用Apache HTTP Client时,当远程证书不可信时,这个异常也会被抛出,而这往往会抛出“sun.security.validator.ValidatorException: PKIX path building failed”。

发生这种情况的原因是因为Apache HTTP客户端在做其他事情之前尝试获取SSLSession和对等证书。

正如一个提醒,有3 ways of initiating the handshake with an 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(); 

这抛出 “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

这是在版本4.2.1中由其org.apache.http.conn.ssl.SSLSocketFactory使用的Apache HTTP Client's AbstractVerifier中实施的逻辑。后续版本make an explicit call to startHandshake(),基于issue HTTPCLIENT-1346中的报告。

这最终似乎来自的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(); 

值得庆幸的是,这仍然会抛出“javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed”,只要你真正尝试(通过获取会话没有得到同行的证书有没有漏洞)使用SSLSocket


如何解决这个

像使用证书的任何其他问题不受信任的,这是确保你正在使用的信任存储包含必要的信任锚的问题(即CA颁发您试图验证的链的证书,或者特殊情况下的实际服务器证书)。

要解决此问题,您应该将CA证书(或可能是服务器证书本身)导入您的信任存储区。在您的信任存储的本地副本

  • 在JRE信任库,通常是cacerts文件(这并不一定是最好的,因为这将使用该JRE会影响所有的应用程序),
  • :你可以这样做(您可以使用-Djavax.net.ssl.trustStore=...选项进行配置),
  • 通过为该连接创建特定SSLContext(如this answer中所述)。 (有人建议用它来,什么也不做的信托经理,但这样会使您的连接容易受到中间人攻击。)

初始答案

javax.net.ssl中。SSLPeerUnverifiedException:同行不认证

这有什么做与信任的证书或您不必创建一个自定义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,否则默认情况下会启用TLS。

+0

嗯,我得到了两个远程证书。将尝试从几个不同的客户端 –

+0

从OSX,这两个工作,从Linux,只有ssl3。将调查服务器配置。 –

+0

这可能与密码套件有关。在你的OSX客户端上,'openssl ciphers DEFAULT'与你的Linux客户端有相同的密码套件吗?一些会在OSX列表中,但不在Linux上? – Bruno

0

创建自定义上下文,以便您可以记录证书无效的原因。或者只是调试到它。