2012-06-12 55 views
46

我正在分布式应用程序中的服务器上工作,该应用程序具有浏览器客户端,并且还参与了与第三方的服务器到服务器通信。 我的服务器有一个CA签名证书,可让我的客户端使用HTTP/S和XMPP(安全)使用TLS(SSL)通信进行连接。这一切工作正常。如何以编程方式设置JAX-WS客户端的SSLContext?

现在我需要通过HTTPS/SSL使用JAX-WS安全地连接到第三方服务器。在此通信中,我的服务器充当JAX-WS interation中的客户端,并且我拥有由第三方签署的客户端证书。

我尝试通过标准系统配置(-Djavax.net.ssl.keyStore=xyz)添加新的密钥库,但我的其他组件明显受此影响。尽管我的其他组件使用专用参数进行SSL配置(my.xmpp.keystore=xxx, my.xmpp.truststore=xxy, ...),但它们似乎最终使用全局SSLContext。 (配置命名空间my.xmpp.似乎表明分离,但情况并非如此)

我也尝试将我的客户端证书添加到我的原始密钥库中,但是 - 我的其他组件似乎并不喜欢它。

我认为我唯一的选择就是以编程方式挂钩到JAX-WS HTTPS配置中,为客户端JAX-WS交互设置密钥库和信任库。

关于如何做到这一点的任何想法/指针?我找到的所有信息都使用javax.net.ssl.keyStore方法,或者设置全局SSLContext,我猜 - 最终会以相同的方式结束。最近我得到了一些有用的东西是这个旧的bug报告,请求我需要的功能:Add support for passing an SSLContext to the JAX-WS client runtime

任何需要?

+0

错误报告说2.1.1版中将提供修复程序。 – EJP

+0

@EJP该错误报告是一个功能请求,并没有提及它应该/将如何完成。 – maasg

回答

47

这一次是一个难啃的骨头,所以备案:

为了解决这个问题,它需要一个定制KeyManager,并使用该定制KeyManager访问分离KeyStore一个SSLSocketFactory。 我发现基本代码这个KeyStoreSSLFactory在这个优秀的博客条目: how-to-dynamically-select-a-certificate-alias-when-invoking-web-services

然后,专门SSLSocketFactory需要插入WebService的背景:

service = getWebServicePort(getWSDLLocation()); 
BindingProvider bindingProvider = (BindingProvider) service; 
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getCustomSocketFactory()); 

getCustomSocketFactory()返回SSLSocketFactory使用上述方法创建。这只适用于JDK中Sun-Oracle impl的JAX-WS RI,因为指定SSLSocketFactory属性的字符串是专用于此实现的。

在这个阶段,JAX-WS服务通信通过SSL进行保护,但是如果您从同一个安全服务器()加载WSDL,那么您将遇到引导问题,如收集WSDL的HTTPS请求将不会使用与Web服务相同的凭据。我工作围绕这一问题通过使WSDL本地可用(文件:/// ...)和动态变化的网络服务端点(为什么这需要一个良好的讨论可以发现in this forum

bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, webServiceLocation); 

现在WebService得到引导,并且能够使用命名(别名)客户端证书和相互身份验证通过SSL与服务器对端通信。∎

+0

如果您的WSDL可以在http://下访问,那么这看起来很有用,但如果它也在https://下呢?客户端在第一步失败(获取WSDL)。 –

+2

我们遇到了同样的情况,最终在本地获得了wsdl的副本。否则,你正是这个引导问题。 – maasg

+0

我在下面添加了关于我的解决方法的新评论。 –

18

这就是我如何根据this post解决它的一些小调整。该解决方案不需要创建任何其他类。

SSLContext sc = SSLContext.getInstance("SSLv3"); 

KeyManagerFactory kmf = 
    KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); 
ks.load(new FileInputStream(certPath), certPasswd.toCharArray()); 

kmf.init(ks, certPasswd.toCharArray()); 

sc.init(kmf.getKeyManagers(), null, null); 

((BindingProvider) webservicePort).getRequestContext() 
    .put(
     "com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", 
     sc.getSocketFactory()); 
16

我尝试以下,并没有对我的工作环境:

bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getCustomSocketFactory()); 

但是不同性质的工作就像一个魅力:

bindingProvider.getRequestContext().put(JAXWSProperties.SSL_SOCKET_FACTORY, getCustomSocketFactory()); 

的代码的其余部分被送往从第一个答复。

+0

什么版本的一切?它看起来像不同版本的'wsimport'生成不同类型的代码......它们可能需要不同的属性名称。 –

+4

当您向应用程序明确添加jaxws-rt JAR时,您需要使用不包含'.internal.'的属性名称。如果您使用JDK中包含的JAXWS-RT,则需要使用包含“.internal”的JAXWS-RT。 'JAXWSProperties.SSL_SOCKET_FACTORY'解析为''com.sun.xml.ws.transport.https.client.SSLSocketFactory“' –

2

以上都没问题(正如我在评论中所说的),除非您的WSDL可以通过https://访问。

这里是我的这种解决方法:

设置你的SSLSocketFactory为默认:

HttpsURLConnection.setDefaultSSLSocketFactory(...); 

对于Apache CXF我用你也需要添加这些行到你的配置:

<http-conf:conduit name="*.http-conduit"> 
    <http-conf:tlsClientParameters useHttpsURLConnectionDefaultSslSocketFactory="true" /> 
<http-conf:conduit> 
0

对于那些尝试,但仍然没有得到它的工作,这使用Wildfly 8,使用动态Dispatcher

bindingProvider.getRequestContext().put("com.sun.xml.ws.transport.https.client.SSLSocketFactory", yourSslSocketFactory);

注意,internal部分从属性关键是在这里了。

+0

我使用Wildfly 8,它也不适用于我。 (你是什么意思与* Dynamic Dispatcher *? - 请检查我的帖子:http://stackoverflow.com/questions/37158821/wildfly-how-to-use-jaxws-ri-instead-of-apache-cxf-webservice -client-only和http://stackoverflow.com/questions/37158821/wildfly-how-to-use-jaxws-ri-instead-of-apache-cxf-webservice-client-only – badera

3

通过结合拉狄克和l0co的回答,您可以访问HTTPS背后的WSDL:

SSLContext sc = SSLContext.getInstance("TLS"); 

KeyManagerFactory kmf = KeyManagerFactory 
     .getInstance(KeyManagerFactory.getDefaultAlgorithm()); 

KeyStore ks = KeyStore.getInstance("JKS"); 
ks.load(getClass().getResourceAsStream(keystore), 
     password.toCharArray()); 

kmf.init(ks, password.toCharArray()); 

sc.init(kmf.getKeyManagers(), null, null); 

HttpsURLConnection 
     .setDefaultSSLSocketFactory(sc.getSocketFactory()); 

yourService = new YourService(url); //Handshake should succeed 
+0

在'getResourceAsStream中是否有'keystore' (keystore)'是指证书文件的路径? – swifthorseman

+0

在这种情况下,它是类路径中的路径。如果需要绝对文件路径,可以使用File而不是getClass()。getResourceAsStream()。 – cidus

0

我不得不建立信任管理器时,信任自签名证书问题。我用的apache的HttpClient的SSLContexts构建器来创建自定义SSLSocketFactory

SSLContext sslcontext = SSLContexts.custom() 
     .loadKeyMaterial(keyStoreFile, "keystorePassword.toCharArray(), keyPassword.toCharArray()) 
     .loadTrustMaterial(trustStoreFile, "password".toCharArray(), new TrustSelfSignedStrategy()) 
     .build(); 
SSLSocketFactory customSslFactory = sslcontext.getSocketFactory() 
bindingProvider.getRequestContext().put(JAXWSProperties.SSL_SOCKET_FACTORY, customSslFactory); 

并在new TrustSelfSignedStrategy()传递作为loadTrustMaterial方法的参数。

相关问题