首先,我将从摘要开始。我正在使用Apache CXF客户端通过SSL与使用自签名证书的Apache CXF服务提供商进行通信。我将证书导入客户端服务器上的WebSphere信任库,但我仍然收到“javax.net.ssl.SSLHandshakeException:SSLHandshakeException,调用https://somesvcprovider.com/appname/svc/myservice:com.ibm.jsse2.util.h:找不到可信证书”异常。
现在,详细信息如下:
我有一个Apache CXF Web服务客户端,我使用Spring进行配置,客户端部署到WebSphere 6.1应用程序服务器。 CXF客户端与不同WebSphere服务器上的Apache CXF服务提供程序进行通信。通信使用SSL。
服务提供商正在使用自签名证书。我已经通过管理控制台将提供者的证书导入客户端服务器上的WebSphere信任库。我通过转到SSL证书和密钥管理>完成了这项工作。 SSL配置> NodeDefaultSSLSettings>密钥库和证书> NodeDefaultTrustStore>签名者证书;然后我使用“从端口检索”工具导入证书。
但是,我在尝试联系服务提供者时仍然收到此错误:“javax.net.ssl.SSLHandshakeException:SSLHandshakeException调用https://somesvcprovider.com/appname/svc/myservice:com.ibm.jsse2.util.h:找不到可信证书”
Spring配置文件如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sec="http://cxf.apache.org/configuration/security"
xmlns:http="http://cxf.apache.org/transports/http/configuration"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://cxf.apache.org/configuration/security
http://cxf.apache.org/schemas/configuration/security.xsd
http://cxf.apache.org/transports/http/configuration
http://cxf.apache.org/schemas/configuration/http-conf.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<http:conduit name="*.http-conduit">
<!-- deactivate HTTPS url hostname verification (localhost, etc) -->
<!-- WARNING ! disableCNcheck=true should not used in production. -->
<http:tlsClientParameters disableCNCheck="true" />
</http:conduit>
<!-- Read properties from property file(s). -->
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!-- The *.spring.properties files are prefixed with a system property
that is set on the WebSphere server. -->
<value>classpath:spring.${my.env}.properties</value>
</list>
</property>
</bean>
<jaxws:client id="myServiceClient"
serviceClass="com.client.stub.cxf.IMyService"
address="${my.svc.url}" />
<bean id="myReport" class="com.client.MyReportRequestor">
<property name="client" ref="myServiceClient"/>
</bean>
</beans>
如上所示,CXF客户端是由Spring通过setter注入的。联系该服务的代码如下:
List<String> formNames = client.retrieveNames(formIdsList);
另外,我不知道这是否相关,但是在运行时检查CXF客户端上的TLSClientParameters对象时,不会返回信任管理器。进行检查的代码如下:
// Get the trust managers for this client.
Client proxy = ClientProxy.getClient(client);
HTTPConduit conduit = (HTTPConduit) proxy.getConduit();
TLSClientParameters tls = conduit.getTlsClientParameters();
TrustManager[] trustManagers = tls.getTrustManagers(); // trustManagers is null
为了让Apache CXF客户端信任自签名证书,我还需要做些什么吗?
我更喜欢不必在配置文件中指定信任库的路径和密码。
谢谢!
答案 0 :(得分:7)
CXF可能使用了错误的SSL套接字工厂。
尝试将此添加到Spring配置中:
<http-conf:conduit name="*.http-conduit">
<http-conf:tlsClientParameters useHttpsURLConnectionDefaultSslSocketFactory="true"/>
</http-conf:conduit>
答案 1 :(得分:2)
看看CXF和WAS是如何工作的,访问Websphere的SSLSocketFactory
并使用出站拦截器将其传递给CXF是相当简单的。
如果您使用以下课程:
public class WebsphereSslOutInterceptor extends AbstractPhaseInterceptor<Message> {
private String sslAlias = null;
public WebsphereSslOutInterceptor() {
super(Phase.SETUP);
}
public void handleMessage(Message message) throws Fault {
Conduit conduit = message.getExchange().getConduit(message);
if (conduit instanceof HTTPConduit) {
HTTPConduit httpConduit = (HTTPConduit)conduit;
String endpoint = (String) message.get(Message.ENDPOINT_ADDRESS);
if (endpoint != null) {
try {
URL endpointUrl = new URL(endpoint);
Map<String, String> connectionInfo = new HashMap<String, String>();
connectionInfo.put(
JSSEHelper.CONNECTION_INFO_REMOTE_HOST,
endpointUrl.getHost());
connectionInfo.put(
JSSEHelper.CONNECTION_INFO_REMOTE_PORT,
Integer.toString(endpointUrl.getPort()));
connectionInfo.put(
JSSEHelper.CONNECTION_INFO_DIRECTION,
JSSEHelper.DIRECTION_OUTBOUND);
SSLSocketFactory factory =
JSSEHelper.getInstance().getSSLSocketFactory(
sslAlias,
connectionInfo,
null);
TLSClientParameters tlsClientParameters = httpConduit.getTlsClientParameters();
if (tlsClientParameters != null) {
tlsClientParameters.setSSLSocketFactory(factory);
}
} catch (MalformedURLException e) {
throw new Fault(e);
} catch (SSLException e) {
throw new Fault(e);
}
}
}
}
public void setSslAlias(String sslAlias) {
this.sslAlias = sslAlias;
}
}
然后,您将能够连接到Websphere的SSLSocketFactory,并可以选择使用“动态出站端点SSL配置”设置来指定任何客户端证书,方法是在jaxws:client
标记中指定拦截器:
<jaxws:client id="proxyName"
serviceClass="proxyClass"
address="${web.service.endpointaddress}">
<jaxws:outInterceptors>
<bean class="my.pkg.WebsphereSslOutInterceptor" />
</jaxws:outInterceptors>
</jaxws:client>
顺便说一句,如果在sslAlias
中声明了WebsphereSslOutInterceptor
属性,则可以根据别名选择客户端证书。
因为这是使用来自Websphere的SSLSocketFactory
,所以信任存储也将在Websphere中使用。
编辑:
我使用了CXF 2.3.6和Websphere 6.1
答案 2 :(得分:2)
beny23的解决方案在WAS7上对我很有用,但有以下修改(原因:httpConduit.getTlsClientParameters()可能为null):
替换此部分:
TLSClientParameters tlsClientParameters = httpConduit.getTlsClientParameters();
if (tlsClientParameters != null) {
tlsClientParameters.setSSLSocketFactory(factory);
}
有了这个:
TLSClientParameters tlsClientParameters = httpConduit.getTlsClientParameters();
if (tlsClientParameters == null) {
tlsClientParameters = new TLSClientParameters();
httpConduit.setTlsClientParameters(tlsClientParameters);
}
tlsClientParameters.setSSLSocketFactory(factory);
答案 3 :(得分:0)
我认为您不能像使用外部组件(Apache CXF)一样使用WAS密钥库。您必须构建并使用your own TrustManager。似乎有几个工作examples左右。