2016-03-15 107 views
3

我收到旧的HTTP transport error: javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name服务错误,因为我需要禁用SNI握手,因为原因。我不想用System.setProperty("jsse.enableSNIExtension", "false");在全球范围内完成,如其他地方所建议的那样,http绑定使用sun.net.www.protocol.https.HttpsClient,我似乎无法改变这一点。我曾尝试方法,如https://erikwramner.wordpress.com/2013/03/27/trust-self-signed-ssl-certificates-and-skip-host-name-verification-with-jax-ws/以sun.net编程和暂时禁用JAX-RS客户端的SNI检查**。HttpsClient

我在那里设置自定义SSLSocketFactories和HostnameVerifiers像这样:

BindingProvider bp = (BindingProvider) webServicePort; 
      Map requestContext = bp.getRequestContext(); 
      requestContext.put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getTrustingSSLSocketFactory()); 
      requestContext.put("com.sun.xml.ws.transport.https.client.SSLSocketFactory", getTrustingSSLSocketFactory()); 
      final NaiveHostnameVerifier naiveHostnameVerifier = new NaiveHostnameVerifier(); 
      requestContext.put("com.sun.xml.internal.ws.transport.https.client.hostname.verifier", naiveHostnameVerifier); 
      requestContext.put("com.sun.xml.ws.transport.https.client.hostname.verifier", naiveHostnameVerifier); 

这根本没有任何影响,因为HTTP客户端似乎没有兑现这些属性。

任何提示赞赏!

回答

0

我有同样的问题...激活java.net.debug系统属性all我已经确定问题是服务器在某种程度上配置错误(它接受协商SNI握手,但不接受我们传递给它的名称名称---我们用来连接它的那个)这经常是服务器配置错误,因为服务器以自己的名字回应unrecognized_name,这导致客户端断开连接(java维护者认为服务器必须知道我们的名字)用来连接它)。

无论如何,这并不是一个HostnameVerifier实例的问题,因为客户端尚未验证服务器名称(这是主机名验证器用来),如握手中所示(实际上,客户端在SERVER HELLO握手)。警告(SSL协议的错误112)后客户端将丢弃该连接。因此,客户端握手应该配置为在此类事件上继续。现在服务器正在断开连接,并且没有在本地进行服务器主机名验证,我们只需要让在来自服务器的警告消息之后继续进行初始握手。由于我目前没有SSLSocketImpl类的源代码,因此我无法进一步调查此问题,因此可能我会再次获得更多信息,一旦我获得源代码并测试是否还有其他方法设置SSLSocketImpl实例的属性,而不是从系统属性配置中获取它。

主要让人困惑的是,SNI握手用于允许服务器在握手过程中使用多域(或虚拟主机),所以它通常配有设计合理的证书(这是失败的)具有备用dns客户端的条目使用了名称。由于名称列表是空的或仅具有主机的规范名称,因此客户端使用的名称无法识别并返回给客户端(应将其解释为警告),但Java的默认实现取决于系统的属性,继续或断开连接。另一种方式来配置每个连接的基础上应该启用,但我还不知道它。

编辑

看的OpenJDK源代码(sorrry,但Oracle的实现并不为ClientHandshaker类发布代码,有仅是一个地方,内场enableSNIextensionprivatefinalstatic场,所以没有机会后在类被初始化后改变它)被初始化,这使得算法只有在你加载了ClientHandshaker类后才能表现出来。

随着该值仅检查以包括在初始helloclient消息可选SNI延伸,它的答案是固定的,所以,只有这样才能绕过它一旦它已默认为true是上配置适当的证书该服务器在备用服务器名称列表中包含正确的服务器名称,或者在服务器中完全禁用SNI协商。您可以查看openJDK here的实现源。

EDIT 2

有一个更复杂的方法,即允许您禁用SNI扩展只是一个连接,并意味着使用SSLConnectionSocketFactory衍生工厂是这样的:

SSLConnectionSocketFactory sslsf = 
    new SSLConnectionSocketFactory( 
      ctx, // specific context, or default 
      SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER // important, see the text below 
    ) { 
       @Override 
       public Socket connectSocket(int connectTimeout, 
              Socket socket, 
              HttpHost host, 
              InetSocketAddress remoteAddress, 
              InetSocketAddress localAddress, 
              HttpContext context) 
        throws IOException { 
        return super.connectSocket(connectTimeout, 
               socket, 
               new HttpHost(remoteAddress.getAddress(), 
                   remoteAddress.getPort()), 
               remoteAddress, 
               localAddress, 
               context); 
       } 

      }; 
CloseableHttpClient cl = 
    HttpClients.custom() 
       .setSSLSocketFactory(sslsf) 
       .build(); 

,你会看,这使用普通地址连接到套接字,所以没有主机名用于传递到远程服务器,有效地禁止SNI。它有一个缺点,你需要使用匹配所有主机名验证符(如上所示)来构造它,因为它将在验证服务器名称时失败(这是在SSLConnectionSocketFactory超类中初始握手完成后失败)使用的地址与服务器在其证书中使用的主机名不匹配)

相关问题