简体   繁体   English

SNI Java 1.8和Jboss SSL握手

[英]SNI Java 1.8 and Jboss SSL Handshake

My question(s): 我的问题:

It would appear one of the differences in the underlying sun.net.www.protocol.https.HttpsClient is the HostNameVerifier implementation - The problem appears to be currently in HttpsClient in the afterConnect method where setHost is not being called, and that doesn't seem to refer to any SSLParameters - my attempt to work around this was to get our WS client invocation code to load our factory, I have been unsuccessful thus far- 在底层sun.net.www.protocol.https.HttpsClient是HostNameVerifier实现中,这似乎是其中的差异之一-该问题似乎是当前在afterConnect方法中的HttpsClient中,其中未调用setHost,并且没有似乎是指任何SSLParameters-我试图解决此问题的方法是让我们的WS客户端调用代码加载我们的工厂,到目前为止,我一直没有成功-

1) How do I get our WS client invocation code to load our factory? 1)如何获取WS客户端调用代码以加载工厂?

OR 要么

2) How do I setup JBOSS to process this request correctly? 2)如何设置JBOSS正确处理此请求? (Assuming it is a JBOSS configuration) (假设它是一个JBOSS配置)

The Stage: - Java Oracle JDK 1.8 64-bit - Jboss 6.4 EAP - AXIS2 - JAX - Certs are Valid and loaded correctly - I am able to replicate the error using a standalone java application and I am able to repair the error with a standalone java application. 阶段:-Java Oracle JDK 1.8 64位-Jboss 6.4 EAP-AXIS2-JAX-证书有效且已正确加载-我能够使用独立的Java应用程序复制错误,并且能够独立修复错误Java应用程序。

Attempts: 尝试次数:

1) Added a CXF out interceptor of PRE-PROTOCOL phase, which will add hostname in header. 1)添加了PRE-PROTOCOL阶段的CXF输出拦截器,它将在标头中添加主机名。

public void handleMessage(Message message) {
       System.out.println("Inside handle message");
       Map<String, List> headers = (Map<String, List>) message.get(Message.PROTOCOL_HEADERS);
       try {
                  headers.put("Host", Collections.singletonList("ecm-users-dev.aexp.com"));
           } catch (Exception ce) {
           throw new Fault(ce);
       }
    }

2) Tried disabling HostNameVerfier as some blogs suggested enabling hostNameverfier would cause issues. 2)尝试禁用HostNameVerfier,因为一些博客建议启用hostNameverfier会导致问题。

3) Created a wrapper around SSLSocketFactory to inject SSL parameters as suggested in below blog http://javabreaks.blogspot.com/2015/12/java-ssl-handshake-with-server-name.html 3)按照下面的博客http://javabreaks.blogspot.com/2015/12/java-建议的那样,围绕SSLSocketFactory创建包装器以注入SSL参数。

4) Injecting host parameters into httpconduit session through client policy. 4)通过客户端策略将主机参数注入httpconduit会话。

final HTTPConduit httpConduit = (HTTPConduit) cxfClient.getConduit();
    final TLSClientParameters tlsCP = new TLSClientParameters();
   HTTPClientPolicy clientPolicy = new HTTPClientPolicy();
   clientPolicy.setHost(endpoint.getHost());
   httpConduit.setClient(clientPolicy);
   httpConduit.setTlsClientParameters(tlsCP);

5) Tried injecting wrapper SSLSocketfactory through binding provider. 5)尝试通过绑定提供程序注入包装器SSLSocketfactory。

What appears to be the issue: Setting the SNI.. 似乎是问题所在:设置SNI。

My logs: 我的日志:

-------Working----- -------工作中-----

14:11:02,417 INFO  [stdout] (http-/127.0.0.1:8080-2) Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
14:11:02,419 I
NFO  [stdout] (http-/127.0.0.1:8080-2) Extension server_name, server_name: [type=host_name (0), value=some.server.value]
14:11:02,419 
INFO  [stdout] (http-/127.0.0.1:8080-2) ***

----Non-working----- ----不工作-----

14:15:35,081 INFO  [stdout] (http-/127.0.0.1:8080-1) Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
14:15:35,082 
INFO  [stdout] (http-/127.0.0.1:8080-1) ***

(this one is missing the SNI) (此人缺少SNI)

(two attempted) code snippets: (两次尝试)代码段:

try{               bp.getRequestContext().put("com.sun.xml.ws.transport.https.client.SSLSocketFactory", new SSLSocketFactoryWrapper(sslContext.getSocketFactory(), sslParameters));
                        } catch (Exception e) {
                            log.error("Error of port default SSL configuration applying", e);
                            throw new IllegalArgumentException("fail to configure ws client by configuration", e);
                           }

-------------------- --------------------

bp.getRequestContext().put("com.sun.xml.ws.transport.https.client.SSLSocketFactory" , new SSLSocketFactoryFacade().createSocket(endPoint,443));
                        log.info("exit getServiceClient(): " + client);
                        return client;
        } catch(Throwable e) {
                        log.error("Error creating the Service Client", e);
                        throw new RuntimeException("Error creating the Service Client: " + e.getMessage(), e);
    }

Similar Question: 类似的问题:

(Suggestion did not work) Extended server_name (SNI Extension) not sent with jdk1.8.0 but send with jdk1.7.0 (建议不起作用) 扩展server_name(SNI扩展)未与jdk1.8.0一起发送,但与jdk1.7.0一起发送

If your WS client is based on resteasy : 如果您的WS客户端基于resteasy:

  • there is bug in w java 8 (see : bugs.java.com/bugdatabase/view_bug.do?bug_id=8144566) w Java 8中有bug(请参阅:bugs.java.com/bugdatabase/view_bug.do?bug_id=8144566)
  • this bug affects deprecated apache httpclient code (see issues.apache.org/jira/browse/HTTPCLIENT-1726) 此错误会影响已弃用的apache httpclient代码(请参阅issue.apache.org/jira/browse/HTTPCLIENT-1726)
  • this deprecated code is used in resteasy ( https://issues.jboss.org/browse/RESTEASY-1089 ) 此不推荐使用的代码用于resteasy( https://issues.jboss.org/browse/RESTEASY-1089

Workaround from https://issues.jboss.org/browse/RESTEASY-1089 : 解决方法来自https://issues.jboss.org/browse/RESTEASY-1089

new ResteasyClientBuilder().httpEngine(new URLConnectionEngine()).build()

The WS client we were using in this case is utilizing AXIS2 and is designed to be agnostic to the framework in use. 在这种情况下,我们使用的WS客户端正在使用AXIS2,并且被设计为与所使用的框架无关。 The SNI issue was being caused by an SSL header that was not being set called: SNI问题是由未设置的SSL标头引起的:

Extension server_name, server_name: 扩展服务器名,服务器名:

Its posted values should look something like this: 其发布的值应如下所示:

[type=host_name (0), value=some.server.value] [type = host_name(0),值= some.server.value]

During out initial trials two failed assumption sets that were made 1) that any httpclient could be used ie the jboss supplied client or the JAVAX supplied client or even the client supplied by AXIS2 – however this was not the case and lead down many roads which were not conducive to a solution. 在最初的试验中,有两个失败的假设集:1)可以使用任何httpclient,即jboss提供的客户端或JAVAX提供的客户端,甚至是AXIS2提供的客户端,但是事实并非如此,并导致了许​​多失败。不利于解决。 We knew with a fair amount of certainty that we needed to set the SNI headers via an SSLFactory, what we were unsure of was how to get that factory to produce connectors that would be used by the system. 我们非常肯定地知道我们需要通过SSLFactory设置SNI标头,但我们不确定的是如何让该工厂生产将被系统使用的连接器。 Something that might have been helpful in identifying this issue would have been: 可能有助于识别此问题的一些事情是:

 Class klass = String.class;
URL location = klass.getResource('/' + klass.getName().replace('.', '/') + ".class");

2) Another area that we were unable to procced forward with was the bind solution presented in multiple wikis and howto's: 2)我们无法进行处理的另一个领域是在多个Wiki和howto中提供的绑定解决方案:

bindingProvider.getRequestContext().put("<a href="http://com.sun.xml.internal.ws">com.sun.xml.internal.ws</a>.transport.https.client.SSLSocketFactory", new SSLSocketFactoryFacade());

The Application had its own version of HTTPclient which needed to be used. 该应用程序具有自己的HTTPclient版本,需要使用。 So a new class called BrokeredEpaasSSLSocketFactory was created that implements SecureProtocolSocketFactory based on the org.apache.commons.httpclient 3.1 supplied by the application. 因此,创建了一个名为BrokeredEpaasSSLSocketFactory的新类,该类基于应用程序提供的org.apache.commons.httpclient 3.1实现SecureProtocolSocketFactory。 The key pieces of code are these - In the client itself: (to restrict the factories usage we have altered the URL's of EPAAS to carry epaas instead of HTTPS) Initially we had thought of using register in the protocol class for https – however that would then mean every application that has a unique URL would need to be tested 关键代码是这些-在客户端本身中:(为限制工厂使用,我们已更改了EPAAS的URL,以使用epaas代替HTTPS) 最初,我们曾考虑过在协议类中使用https来注册-但是这样做那么意味着每个具有唯一URL的应用程序都需要进行测试

Protocol myHTTPS = new Protocol( "https", new MySSLSocketFactory(), 443 );
 Protocol.registerProtocol( "https", myHTTPS );

Also an attempt to try and alter the protocol (using epaas to make the request instead of https) resulted in AXIS throwing back an error that wouldn't allow to proceed forward so we abandoned this idea… 尝试更改协议的尝试(使用epaas代替https发出请求)也导致AXIS抛出了错误,不允许继续进行操作,因此我们放弃了这个主意……

String endPointFinal = endPoint.replaceAll("epaas://", "https://");
          bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endPointFinal);
          bp.getRequestContext().put("SO_TIMEOUT", new Integer(sTimout));
          bp.getRequestContext().put("CONNECTION_TIMEOUT", new Integer(cTimeout));
          BrokeredEpaasSSLSocketFactory factory = new BrokeredEpaasSSLSocketFactory();   
          Protocol authhttps = new Protocol("https", factory, 443);
          HttpClient httpclient = new HttpClient();
          URL URI = new URL(endPointFinal);
          httpclient.getHostConfiguration().setHost(URI.getHost(), 443, authhttps);
          GetMethod httpget  = new GetMethod("/ecmuser/BrokeredConnectorService");
          httpclient.executeMethod(httpget);

The newly created class (BrokeredEpaasSSLSocketFactory) has two methods of particular interest: This currently is the only createSocket being used by the application (though they have all been altered for future use): 新创建的类(BrokeredEpaasSSLSocketFactory)具有两个特别令人感兴趣的方法:这是应用程序正在使用的唯一createSocket(尽管它们都已更改以供将来使用):

public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
        // TODO Auto-generated method stub
        if (params == null) {
            throw new IllegalArgumentException("Parameters may not be null");
        }
        int timeout = params.getConnectionTimeout();
        if (timeout == 0) {
            return _defaultFactory.createSocket(host, port,localAddress,localPort);
        } else {

            SSLSocket socket = (SSLSocket) _defaultFactory.createSocket();
            setParameters(socket,host);
            SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
            SocketAddress remoteaddr = new InetSocketAddress(host, port);
            socket.bind(localaddr);
            socket.connect(remoteaddr, timeout);
            return socket;
        }
    }

This is the method which actually sets the SNI headers on the SSL outbound request: 这是在SSL出站请求上实际设置SNI标头的方法:

private void setParameters(SSLSocket socket, String host)
        throws MalformedURLException {
    SSLParameters sslParameters = new SSLParameters();
    List<SNIServerName> sniHostNames = new ArrayList<SNIServerName>(1);
    sniHostNames.add(new SNIHostName(host));
    sslParameters.setServerNames(sniHostNames);
    socket.setSSLParameters(sslParameters);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM