2012-06-12 87 views
46

我正在分佈式應用程序中的服務器上工作,該應用程序具有瀏覽器客戶端,並且還參與了與第三方的服務器到服務器通信。 我的服務器有一個CA簽名證書,可讓我的客戶端使用HTTP/S和XMPP(安全)使用TLS(SSL)通信進行連接。這一切工作正常。如何以編程方式設置JAX-WS客戶端的SSLContext?

現在我需要通過HTTPS/SSL使用JAX-WS安全地連接到第三方服務器。在此通信中,我的服務器充當JAX-WS interation中的客戶端,並且我擁有由第三方簽署的客戶端證書。

我嘗試通過標準系統配置(-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,我猜 - 最終會以相同的方式結束。最近我得到了一些有用的東西是這個舊的bug報告,請求我需要的功能:Add support for passing an SSLContext to the JAX-WS client runtime

任何需要?

+0

錯誤報告說2.1.1版中將提供修復程序。 – EJP

+0

@EJP該錯誤報告是一個功能請求,並沒有提及它應該/將如何完成。 – maasg

回答

47

這一次是一個難啃的骨頭,所以備案:

爲了解決這個問題,它需要一個定製KeyManager,並使用該定製KeyManager訪問分離KeyStore一個SSLSocketFactory。 我發現基本代碼這個KeyStoreSSLFactory在這個優秀的博客條目: how-to-dynamically-select-a-certificate-alias-when-invoking-web-services

然後,專門SSLSocketFactory需要插入WebService的背景:

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

getCustomSocketFactory()返回SSLSocketFactory使用上述方法創建。這隻適用於JDK中Sun-Oracle impl的JAX-WS RI,因爲指定SSLSocketFactory屬性的字符串是專用於此實現的。

在這個階段,JAX-WS服務通信通過SSL進行保護,但是如果您從同一個安全服務器()加載WSDL,那麼您將遇到引導問題,如收集WSDL的HTTPS請求將不會使用與Web服務相同的憑據。我工作圍繞這一問題通過使WSDL本地可用(文件:/// ...)和動態變化的網絡服務端點(爲什麼這需要一個良好的討論可以發現in this forum

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

現在WebService得到引導,並且能夠使用命名(別名)客戶端證書和相互身份驗證通過SSL與服務器對端通信。∎

+0

如果您的WSDL可以在http://下訪問,那麼這看起來很有用,但如果它也在https://下呢?客戶端在第一步失敗(獲取WSDL)。 –

+2

我們遇到了同樣的情況,最終在本地獲得了wsdl的副本。否則,你正是這個引導問題。 – maasg

+0

我在下面添加了關於我的解決方法的新評論。 –

18

這就是我如何根據this post解決它的一些小調整。該解決方案不需要創建任何其他類。

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

我嘗試以下,並沒有對我的工作環境:

bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getCustomSocketFactory()); 

但是不同性質的工作就像一個魅力:

bindingProvider.getRequestContext().put(JAXWSProperties.SSL_SOCKET_FACTORY, getCustomSocketFactory()); 

的代碼的其餘部分被送往從第一個答覆。

+0

什麼版本的一切?它看起來像不同版本的'wsimport'生成不同類型的代碼......它們可能需要不同的屬性名稱。 –

+4

當您嚮應用程序明確添加jaxws-rt JAR時,您需要使用不包含'.internal.'的屬性名稱。如果您使用JDK中包含的JAXWS-RT,則需要使用包含「.internal」的JAXWS-RT。 'JAXWSProperties.SSL_SOCKET_FACTORY'解析爲''com.sun.xml.ws.transport.https.client.SSLSocketFactory「' –

2

以上都沒問題(正如我在評論中所說的),除非您的WSDL可以通過https://訪問。

這裏是我的這種解決方法:

設置你的SSLSocketFactory爲默認:

HttpsURLConnection.setDefaultSSLSocketFactory(...); 

對於Apache CXF我用你也需要添加這些行到你的配置:

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

對於那些嘗試,但仍然沒有得到它的工作,這使用Wildfly 8,使用動態Dispatcher

bindingProvider.getRequestContext().put("com.sun.xml.ws.transport.https.client.SSLSocketFactory", yourSslSocketFactory);

注意,internal部分從屬性關鍵是在這裏了。

+0

我使用Wildfly 8,它也不適用於我。 (你是什麼意思與* Dynamic Dispatcher *? - 請檢查我的帖子:http://stackoverflow.com/questions/37158821/wildfly-how-to-use-jaxws-ri-instead-of-apache-cxf-webservice -client-only和http://stackoverflow.com/questions/37158821/wildfly-how-to-use-jaxws-ri-instead-of-apache-cxf-webservice-client-only – badera

3

通過結合拉狄克和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 
+0

在'getResourceAsStream中是否有'keystore' (keystore)'是指證書文件的路徑? – swifthorseman

+0

在這種情況下,它是類路徑中的路徑。如果需要絕對文件路徑,可以使用File而不是getClass()。getResourceAsStream()。 – cidus

0

我不得不建立信任管理器時,信任自簽名證書問題。我用的apache的HttpClient的SSLContexts構建器來創建自定義SSLSocketFactory

SSLContext sslcontext = SSLContexts.custom() 
     .loadKeyMaterial(keyStoreFile, "keystorePassword.toCharArray(), keyPassword.toCharArray()) 
     .loadTrustMaterial(trustStoreFile, "password".toCharArray(), new TrustSelfSignedStrategy()) 
     .build(); 
SSLSocketFactory customSslFactory = sslcontext.getSocketFactory() 
bindingProvider.getRequestContext().put(JAXWSProperties.SSL_SOCKET_FACTORY, customSslFactory); 

並在new TrustSelfSignedStrategy()傳遞作爲loadTrustMaterial方法的參數。

相關問題