2015-06-13 144 views
17

我已经使用ApacheCXF(v3.0.4)实现了一个JAX-WS客户端,并且一切正常,但问题出现在我想要使用与java 8(jdk1.8.0_25)的安全连接(SSL/TLS)。扩展服务器名称(SNI扩展)不与jdk1.8.0一起发送,但使用jdk1.7.0发送

我看到在日志中出现以下异常(-Djavax.net.debug =全部):

main, handling exception: java.net.SocketException: Connection reset 
main, SEND TLSv1.2 ALERT: fatal, description = unexpected_message 
main, WRITE: TLSv1.2 Alert, length = 2 
main, Exception sending alert: java.net.SocketException: Connection reset by peer: socket write error 

后,我已经注意到了此问题,是因为引起了depeer分析与Java 8中的服务器名(SNI)不发送,但使用Java 7发送并且Web服务调用成功运行。

的Java 8日志(-Djavax.net.debug =全部):缺少 “扩展服务器名”

[...] 
Compression Methods: { 0 } 
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1} 
Extension ec_point_formats, formats: [uncompressed] 
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA 
*** 
[...] 

Java 7的日志(-Djavax.net.debug =全部)(作品): “扩展服务器名” 被设置

[...] 
Compression Methods: { 0 } 
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1} 
Extension ec_point_formats, formats: [uncompressed] 
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA 
Extension server_name, server_name: [host_name: testeo.hostname.es] 
*** 
[...] 

据观察,与Java 7 扩展服务器名,服务器名:[HOST_NAME:testeo.hostname.es]被设置,然后将带状片S服务调用成功。

为什么Java 8没有像Java 7那样设置server_name?它是一个Java配置问题?

+0

欢迎来到Stack Overflow!与论坛网站不同,我们不使用“谢谢”或“任何帮助表示赞赏”,或在[so]上签名。请参阅“[应该'嗨','谢谢',标语和致敬从帖子中删除?](http://meta.stackexchange.com/questions/2950/should-hi-thanks-taglines-and-salutations-be ),这是“预先感谢”,而不是“感谢先进” –

回答

0

首先,这个“server_name”的东西与SNI(服务器名称指示)扩展相关联。 Java 8 JSSE文档谈论它here

该文档包含示例代码,显示如何设置发送的服务器名称。代码是用于Java 8.

但是,我不明白为什么(显然)Java 7默认设置服务器名称,而Java 8不是。 (最简单的办法弄明白是使用调试器来找出如何创建SSL引擎对象和初始化)。

+0

我遇到过类似的问题,java7没有在某些情况下设置它,并将其设置在其他地方 – CasualT

6

你,或底层库(的WS LIB做的话)可能是使用setHostnameVerifier( ..)

在java8中有一个错误,如果使用setHostnameVerifier(..),则SNI不是从客户端完成的。

https://bugs.openjdk.java.net/browse/JDK-8072464

12

如前所述,原因是在哪里使用setHostnameVerifier()打破SNI(扩展服务器名)相关的JDK错误。 https://bugs.openjdk.java.net/browse/JDK-8144566

我们的解决方法: 经过测试我们发现,设置连接的SSLSocketFactory的只是关于从默认的东西似乎解决这个问题。

这不起作用: HttpsURLConnection.setSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault());

这并不工作: HttpsURLConnection.setSSLSocketFactory(new SSLSocketFactoryFacade());

因此,要解决它的一个JAX-WS客户端,你可以做这样的事情: bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", new SSLSocketFactoryFacade());

我们的SSLSocketFactory正面:(请注意它确实没有任何作用)

public class SSLSocketFactoryFacade extends SSLSocketFactory { 

    SSLSocketFactory sslsf; 

    public SSLSocketFactoryFacade() { 
     sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault();; 
    } 

    @Override 
    public String[] getDefaultCipherSuites() { 
     return sslsf.getDefaultCipherSuites(); 
    } 

    @Override 
    public String[] getSupportedCipherSuites() { 
     return sslsf.getSupportedCipherSuites(); 
    } 

    @Override 
    public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException { 
     return sslsf.createSocket(socket, s, i, b); 
    } 

    @Override 
    public Socket createSocket(String s, int i) throws IOException, UnknownHostException { 
     return sslsf.createSocket(s, i); 
    } 

    @Override 
    public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) throws IOException, UnknownHostException { 
     return sslsf.createSocket(s, i, inetAddress, i1); 
    } 

    @Override 
    public Socket createSocket(InetAddress inetAddress, int i) throws IOException { 
     return createSocket(inetAddress, i); 
    } 

    @Override 
    public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException { 
     return createSocket(inetAddress, i, inetAddress1, i1); 
    } 
} 
+0

请解释什么是绑定提供程序和/或添加代码来演示我想用你的解决方案来修复现有系统中的这个bug – chrisinmtown

+0

这个门面类的技巧和我自定义的HostnameVerifier一起修复了我的应用程序在JDK8中。 – Whome

+0

我有个例外:每当我重新启动PC并用客户端应用程序启动Tomcat时,“BuilderException:无法找到有效的请求目标的证书路径”。重新启动Tomcat后,一切正常。插入日志中,我发现由于某种原因,扩展server_name在第一次运行期间不在ClientHello中。自定义SSLSocketFactoryFacade解决了这个问题。 BTW Lombok的@Delegate注释将使这个类超小。 – Yuriy

3

请解决此问题时,请使用JDK 8u141及更高版本。请查看 JDK 8u141 Bugs Fixes page for more details