如何以编程方式设置JAX-WS客户端的SSLContext?

maa*_*asg 54 java ssl certificate jax-ws

我正在一个分布式应用程序中的服务器上工作,该应用程序具有浏览器客户端,并且还参与与第三方的服务器到服务器通信.我的服务器有一个CA签名证书,让我的客户端使用HTTP/S和XMPP(安全)使用TLS(SSL)通信进行连接.这一切都很好.

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

我尝试通过标准系统配置(-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- 我猜 - 将最终在同一个confilc中.我得到的最接近的东西是这个旧的错误报告请求我需要的功能:添加支持将SSLContext传递给JAX-WS客户端运行时

任何需要?

maa*_*asg 55

这个是一个难以破解的难题,所以为了记录:

要解决此问题,它需要一个自定义KeyManager和一个SSLSocketFactory使用此自定义KeyManager来访问分离的自定义KeyStore.我发现基码本KeyStoreSSLFactory对这个优秀的博客条目: 如何对动态选择-一个证书别名时,调用的Web服务

然后,SSLSocketFactory需要将特殊插入到WebService上下文中:

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

使用上述方法创建的getCustomSocketFactory()返回值SSLSocketFactory.这仅适用于内置于JDK中的Sun-Oracle impl的JAX-WS RI,因为指示该SSLSocketFactory属性的字符串是此实现的专有权.

在此阶段,JAX-WS服务通信通过SSL加以保护,但如果您从同一安全服务器()加载WSDL,那么您将遇到引导问题,因为收集WSDL的HTTPS请求将不会使用与Web Service相同的凭据.我通过使WSDL在本地可用(file:/// ...)并动态更改Web服务端点来解决这个问题:( 在此论坛中可以找到关于为什么需要这个的好讨论)

bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, webServiceLocation); 
Run Code Online (Sandbox Code Playgroud)

现在WebService被引导并且能够使用命名(别名)客户端证书和相互身份验证通过SSL与服务器对应方进行通信.∎

  • *仅供参考:*如果您的应用程序使用 JAXWS-RI,那么您应该使用稍微不同的属性:`com.sun.xml.ws.transport.https.client.SSLSocketFactory`。我指定两者是为了满足单元测试和实际应用环境中的不同堆栈(不要问)。 (4认同)
  • 如果博客出现故障,我会在这里存档:http://archive.is/f3goz (3认同)
  • 仅当您的 WSDL 可在 http:// 下访问时,这看起来才有用,但如果它也在 https:// 下怎么办?客户端在第一步(获取 WSDL)失败。 (2认同)
  • 我们遇到了同样的情况,最终在本地获得了wsdl的副本.否则你就会遇到这个引导问题. (2认同)

小智 21

这是我根据这篇文章通过一些小调整解决它的方法.此解决方案不需要创建任何其他类.

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() );
Run Code Online (Sandbox Code Playgroud)


小智 20

我尝试了以下内容,但它对我的环境无效:

bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getCustomSocketFactory());
Run Code Online (Sandbox Code Playgroud)

但不同的财产就像一个魅力:

bindingProvider.getRequestContext().put(JAXWSProperties.SSL_SOCKET_FACTORY, getCustomSocketFactory());
Run Code Online (Sandbox Code Playgroud)

其余代码取自第一个回复.

  • 当您向应用程序显式添加jaxws-rt JAR时,您需要使用不包含`.internal`的属性名称.如果使用JDK中包含的JAXWS-RT,则需要使用包含".internal"的那些.`JAXWSProperties.SSL_SOCKET_FACTORY`解析为`"com.sun.xml.ws.transport.https.client.SSLSocketFactory"` (7认同)

cid*_*dus 5

通过结合 Radek 和 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
Run Code Online (Sandbox Code Playgroud)