【问题标题】:Random "peer not authenticated" exceptions with Java SSLContextImpl$TLS10ContextJava SSLContextImpl$TLS10Context 的随机“peer not authenticated”异常
【发布时间】:2013-08-18 06:02:49
【问题描述】:

我在使用 SSL 连接到 HAProxy 服务器时随机出现连接失败。我已确认这些故障发生在 JDK 版本 1.7.0_21 和 1.7.0_25 上,但不会发生在 1.7.0_04 或 1.6.0_38 上。

例外是

 Exception in thread "main" javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397)
    at SSLTest2.main(SSLTest2.java:52)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

这些故障仅在使用 TLS SSL 上下文而不是默认上下文时发生。以下代码在循环中运行一千次,并且在循环完成之前发生故障(大约 2% 的连接失败):

SSLContext sslcontext = SSLContext.getInstance("TLS");   
sslcontext.init(null, null, null);
SSLSocketFactory factory = sslcontext.getSocketFactory(); 
SSLSocket socket = (SSLSocket)factory.createSocket("myserver", 443);

//socket.startHandshake();
SSLSession session = socket.getSession();
session.getPeerCertificates();
socket.close();

但是,如果我以这种方式创建 SSL 上下文,则在我提到的任何 Java 版本上都没有连接失败:

SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault();

第一种方式使用SSLContextImpl$TLS10Context,后一种方式使用SSLContextImpl$DefaultSSLContext。查看代码,我没有看到任何会导致异常发生的差异。

为什么我会失败?使用getDefault() 调用的优点/缺点是什么?

注意:首先使用 Apache HttpClient(版本 4)发现异常。此代码是重现 HttpClient 所见问题的最小子集。

这是我在添加 -Djavax.net.debug=ssl 时看到的错误:

main, READ: TLSv1 Alert, length = 2
main, RECV TLSv1 ALERT:  fatal, bad_record_mac
%% Invalidated:  [Session-101, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA]
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLException: Received fatal alert:   bad_record_mac
main, IOException in getSession():  javax.net.ssl.SSLException: Received fatal alert: bad_record_mac

另一条信息是,如果我在代理服务器上关闭 Diffie-Hellman,则不会出现错误。

【问题讨论】:

  • 请使用 -Djavax.net.debug=ssl 重新运行,握手并发布失败运行的输出。
  • 打开调试选项再次运行。使用此信息更新了问题。有人有什么想法吗?
  • 为什么你更喜欢四行代码而不是一行代码?
  • @EJP - 你能解释一下你的评论是什么意思吗?我很困惑。
  • 看起来这是上述 Java 版本中的一个错误。从 Eclipse (subclipse) 中连接到 SVN 服务器时,我经常遇到此错误。但是使用最新的 JVM 1.7.0_51 上面的代码不会在这里触发任何异常(执行 1000 次)。

标签: java ssl haproxy sslsocketfactory


【解决方案1】:

从症状来看,我猜测这与使用 TLS false start 的浏览器有关,这是 Google 向 reduce the back-and-forth in TLS 引入的客户端技巧:

False Start 主要由浏览器控制,其工作原理是将官方 SSL 规范中描述的数据的两次往返传递减少为一次往返传递。它通过指示客户端在单个调度中发送 Finished 和第一个 ApplicationData 消息来做到这一点,而不是将它们放在两个不同的包中,然后仅在得到服务器的确认后才发送第二个包。

Google 提议将 False Start 作为官方标准,以使 SSL 更适合目前发现其提供成本太高的网站。通过缩短协商加密密钥和保护最终用户和网站之间传输数据所需的其他变量的握手,False Start 旨在降低许多人所说的使用该协议的性能损失。

来自relevant issue raised in Mozilla Firefox:(强调我的)

到目前为止,已知当前或以前存在与 False Start 兼容性问题的不完整产品列表包括 (AFAICT):F5、A10、Microsoft TMG、Cisco ASA、ServerIron ADX、ESET、NetNanny、一些Java 的 SSL 服务器实现的配置

【讨论】:

  • 在 OP 和我的情况下,连接是在用 JAVA 编写的客户端和服务器(代理或 http)之间。这里没有浏览器...
  • @LorenzoDematté 啊,当然,你是对的。不敢相信我错过了。
【解决方案2】:

javax.net.ssl.SSLPeerUnverifiedException 的出现只是因为 http 安全性,您必须将连接配置为 https,否则请遵循此代码..

            SSLContext ctx = SSLContext.getInstance("TLS");
    ctx.init(null, new TrustManager[]{tm}, null);
    SSLSocketFactory ssf = new SSLSocketFactory(ctx);
    ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    ClientConnectionManager ccm = client.getConnectionManager();
    SchemeRegistry sr = ccm.getSchemeRegistry();
    sr.register(new Scheme("https", ssf, 443));
    return new DefaultHttpClient(ccm, client.getParams());

使用这个。希望对你有帮助

【讨论】:

  • 你只是用ALLOW_ALL_HOSTNAME_VERIFIER 引入了一个安全漏洞,这没有帮助。
  • 它根本不会“仅仅因为 HTTP 安全性而出现”。它与 HTTPS 无关。它发生在 TLS 层。
猜你喜欢
  • 1970-01-01
  • 2011-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多