2011-09-01 75 views
9

首先,我將從總結開始。我正在使用Apache CXF客戶端通過SSL與使用自簽名證書的Apache CXF服務提供者進行通信。我將證書導入到客戶端服務器上的WebSphere信任庫中,但我仍然收到「javax.net.ssl.SSLHandshakeException:調用了SSLHandshakeException https://somesvcprovider.com/appname/svc/myservice:com.ibm.jsse2.util.h:沒有發現可信證書」異常。如何設置Apache CXF客戶端以使用WebSphere信任庫? (接收「找不到可信證書」異常。)

現在,這裏的細節:

我有我使用Spring配置一個Apache CXF Web服務客戶端,客戶端部署到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

我不認爲你可以像使用外部組件(Apache CXF)那樣使用WAS密鑰庫。你必須建立和使用your own TrustManager。這似乎有幾個工作examples

+0

我找不到使用WAS truststores的方法,所以我會假設您必須在配置文件中指定truststore,如您所述。 –

+0

我發現@kjetil解決方案更可行,因爲它不需要自定義開發 –

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

+0

只要我將攔截器定義保留在端點配置中,該解決方案就可以用於我。但是,如果我把它放在總線上,handleMessage方法不會被調用。有人知道這是爲什麼嗎?我寧願不必爲每個端點指定相同的攔截器。 – Sandman

+0

@ beny23如果我使用Websphere動態端點,是否仍需要所有的Interceptor和SSLSocketFactory?我的理解是,我可以從我的代碼中進行簡單的SSL調用,並且Dynamic Endpoint應該攔截基於URL匹配的所有傳出請求。那是對的嗎? – CodeClimber

7

CXF很可能使用了錯誤的SSL套接字工廠。

嘗試添加以下內容到Spring配置:

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

謝謝,這對我有用! - [WAS 8] [CXF 2.7.18] – DaniEll

+0

也適用於我...即使使用ProxyFactoryBean創建客戶端時,也是如此[WAS 8.5] [CXF 3.1.8] –

2

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); 
相關問題