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

2022-09-04 23:39:56

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

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

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

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

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

关于如何做到这一点的任何想法/指示?我找到的所有信息要么使用该方法,要么正在设置全局 - 我猜 - 将在同一个confilc中结束。我最接近有用的东西是这个旧的错误报告,它请求我需要的功能:添加对将SSLContext传递到JAX-WS客户机运行时的支持javax.net.ssl.keyStoreSSLContext

有什么需要吗?


答案 1

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

为了解决这个问题,它需要一个自定义和一个使用此自定义来访问分离的.我在这个优秀的博客文章中找到了这个的基本代码:如何动态选择证书别名调用Web服务KeyManagerSSLSocketFactoryKeyManagerKeyStoreKeyStoreSSLFactory

然后,需要将专用插入到 Web 服务上下文中:SSLSocketFactory

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

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

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

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

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


答案 2

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

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() );