【问题标题】:How to set up Apache CXF client to use WebSphere truststore? (Receiving "No trusted certificate found" exception.)如何设置 Apache CXF 客户端以使用 WebSphere 信任库? (收到“未找到受信任的证书”异常。)
【发布时间】:2011-09-01 18:55:52
【问题描述】:

首先,我将从摘要开始。我正在使用 Apache CXF 客户端通过 SSL 与使用自签名证书的 Apache CXF 服务提供商进行通信。我将证书导入到客户端服务器上的 WebSphere 信任库中,但我仍然收到“javax.net.ssl.SSLHandshakeException: SSLHandshakeException invoking https://somesvcprovider.com/appname/svc/myservice: com.ibm.jsse2.util.h: No trusted certificate found”异常。

现在,这是详细信息:

我有一个使用 Spring 配置的 Apache CXF Web 服务客户端,该客户端部署到 WebSphere 6.1 应用程序服务器。 CXF 客户端与不同 WebSphere 服务器上的 Apache CXF 服务提供者通信。通信使用 SSL。

服务提供商正在使用自签名证书。我已经通过管理控制台将提供者的证书导入到客户端服务器上的 WebSphere 信任库中。我通过转到 SSL 证书和密钥管理 > SSL 配置 > NodeDefaultSSLSettings > 密钥库和证书 > NodeDefaultTrustStore > 签名者证书来完成此操作;然后我使用“从端口检索”工具导入证书。

但是,我在尝试联系服务提供商时仍然收到此错误:“javax.net.ssl.SSLHandshakeException: SSLHandshakeException invoking https://somesvcprovider.com/appname/svc/myservice: com.ibm.jsse2.util.h: No trust trust found”。

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 客户端信任自签名证书?

我希望不必在配置文件中指定信任库的路径以及密码。

谢谢!

【问题讨论】:

    标签: ssl websphere cxf websphere-6.1


    【解决方案1】:

    CXF 可能使用了错误的 SSL 套接字工厂。

    尝试将此添加到您的 Spring 配置中:

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

    【讨论】:

    • 对我也有用...即使使用 ProxyFactoryBean 以动态方式创建客户端 [WAS 8.5] [CXF 3.1.8]
    【解决方案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>
    

    顺便说一句,如果在WebsphereSslOutInterceptor 中声明了sslAlias 属性,则可以根据其别名选择客户端证书。

    因为这是使用来自 Websphere 的 SSLSocketFactory,所以也将使用来自 Websphere 的信任存储。

    编辑:

    我使用的是 CXF 2.3.6 和 Websphere 6.1

    【讨论】:

    • 只要我在端点配置中保留拦截器定义,这个解决方案就对我有用。但是,如果我把它放在总线上,handleMessage 方法就不会被调用。有人知道这是为什么吗?我宁愿不必为每个端点指定相同的拦截器。
    • @beny23 如果我使用 Websphere Dynamic Endpoints,我还需要所有 Interceptor 和 SSLSocketFactory 的东西吗?我的理解是我可以从我的代码中进行一个简单的 SSL 调用,并且动态端点应该拦截所有基于 URL 匹配的传出请求。对吗?
    【解决方案3】:

    beny23 的解决方案在 WAS7 上非常适合我,但做了以下修改(原因:httpConduit.getTlsClientParameters() 可能为空):

    替换这部分:

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

    【讨论】:

      【解决方案4】:

      我认为您不能像使用外部组件 (Apache CXF) 那样使用 WAS 密钥库。您可能必须构建和使用your own TrustManager。似乎有几个正在工作的examples

      【讨论】:

      • 我也找不到使用 WAS 信任库的方法,所以我假设您必须按照您提到的那样在配置文件中指定信任库。
      • 我发现@kjetil 解决方案更可行,因为它不需要自定义开发
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-14
      • 1970-01-01
      • 1970-01-01
      • 2012-11-03
      • 2015-01-15
      • 2016-11-05
      相关资源
      最近更新 更多