ブラウザクライアントを備え、サードパーティとのサーバー間通信にも参加する分散アプリケーションのサーバーで作業しています。私のサーバーには、クライアントがHTTP/SおよびXMPP(secure)を使用したTLS(SSL)通信を使用して接続できるようにするCA署名付き証明書があります。それはすべて正常に動作しています。
次に、JAX-WS over HTTPS/SSLを使用して、サードパーティのサーバーに安全に接続する必要があります。この通信では、サーバーはJAX-WS相互作用でクライアントとして機能し、サードパーティによって署名されたクライアント証明書を持っています。
標準のシステム構成(-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
を設定しています。役立つものに最も近いのは、必要な機能を要求するこの古いバグレポートです。 SSLContextをJAX-WSクライアントランタイムに渡すためのサポートを追加
テイクはありますか?
これはひび割れにくいので、記録のために:
これを解決するには、カスタムKeyManager
と、このカスタムSSLSocketFactory
を使用して分離されたKeyManager
にアクセスするKeyStore
が必要でした。このKeyStore
とSSLFactory
の基本コードは、この優れたブログエントリで見つかりました。 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
を返します。 SSLSocketFactory
プロパティを示す文字列がこの実装に独自のものである場合、これはJDKに組み込まれたSun-Oracle implのJAX-WS RIでのみ機能します。
この段階では、JAX-WSサービスの通信はSSLで保護されていますが、同じ安全なサーバー()からWSDLをロードする場合、収集するHTTPS要求としてbootstrap問題が発生しますWSDLはWebサービスと同じ資格情報を使用しません。この問題を回避するには、WSDLをローカルで使用可能にし(file:/// ...)、Webサービスエンドポイントを動的に変更します(これが必要な理由についての適切な議論が見つかります このフォーラムで )
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, webServiceLocation);
これで、WebServiceがブートストラップされ、名前付き(エイリアス)クライアント証明書と相互認証を使用して、SSLを介してサーバーと通信できるようになりました。 ∎
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() );
私は以下を試しましたが、私の環境では動作しませんでした:
bindingProvider.getRequestContext().put("com.Sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getCustomSocketFactory());
しかし、異なるプロパティが魅力のように機能しました:
bindingProvider.getRequestContext().put(JAXWSProperties.SSL_SOCKET_FACTORY, getCustomSocketFactory());
コードの残りの部分は、最初の返信から取得されました。
Radekと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
Https://でもWSDLにアクセスできる場合を除き、上記は問題ありません(コメントで述べたように)。
これは私の回避策です:
SSLSocketFactoryをデフォルトとして設定します。
HttpsURLConnection.setDefaultSSLSocketFactory(...);
私が使用するApache CXFの場合は、次の行も設定に追加する必要があります。
<http-conf:conduit name="*.http-conduit">
<http-conf:tlsClientParameters useHttpsURLConnectionDefaultSslSocketFactory="true" />
<http-conf:conduit>
試してみてもまだ動かない人のために、これはWildfly 8で動的Dispatcher
を使用して行いました:
bindingProvider.getRequestContext().put("com.Sun.xml.ws.transport.https.client.SSLSocketFactory", yourSslSocketFactory);
Propertyキーのinternal
部分がここになくなっていることに注意してください。
私はここで手順を試しました:
http://jyotirbhandari.blogspot.com/2011/09/Java-error-invalidalgorithmparameterexc.html
そして、それは問題を修正しました。いくつかの微調整を行いました-System.getPropertyを使用して2つのパラメーターを設定します...
プロキシ認証とSSLスタッフをSOAPハンドラーに移動できます
port = new SomeService().getServicePort();
Binding binding = ((BindingProvider) port).getBinding();
binding.setHandlerChain(Collections.<Handler>singletonList(new ProxyHandler()));
これは私の例です、すべてのネットワーク操作を行います
class ProxyHandler implements SOAPHandler<SOAPMessageContext> {
static class TrustAllHost implements HostnameVerifier {
public boolean verify(String urlHostName, SSLSession session) {
return true;
}
}
static class TrustAllCert implements X509TrustManager {
public Java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(Java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(Java.security.cert.X509Certificate[] certs, String authType) {
}
}
private SSLSocketFactory socketFactory;
public SSLSocketFactory getSocketFactory() throws Exception {
// just an example
if (socketFactory == null) {
SSLContext sc = SSLContext.getInstance("SSL");
TrustManager[] trustAllCerts = new TrustManager[] { new TrustAllCert() };
sc.init(null, trustAllCerts, new Java.security.SecureRandom());
socketFactory = sc.getSocketFactory();
}
return socketFactory;
}
@Override public boolean handleMessage(SOAPMessageContext msgCtx) {
if (!Boolean.TRUE.equals(msgCtx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)))
return true;
HttpURLConnection http = null;
try {
SOAPMessage outMessage = msgCtx.getMessage();
outMessage.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "UTF-8");
// outMessage.setProperty(SOAPMessage.WRITE_XML_DECLARATION, true); // Not working. WTF?
ByteArrayOutputStream message = new ByteArrayOutputStream(2048);
message.write("<?xml version='1.0' encoding='UTF-8'?>".getBytes("UTF-8"));
outMessage.writeTo(message);
String endpoint = (String) msgCtx.get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY);
URL service = new URL(endpoint);
Proxy proxy = Proxy.NO_PROXY;
//Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("{proxy.url}", {proxy.port}));
http = (HttpURLConnection) service.openConnection(proxy);
http.setReadTimeout(60000); // set your timeout
http.setConnectTimeout(5000);
http.setUseCaches(false);
http.setDoInput(true);
http.setDoOutput(true);
http.setRequestMethod("POST");
http.setInstanceFollowRedirects(false);
if (http instanceof HttpsURLConnection) {
HttpsURLConnection https = (HttpsURLConnection) http;
https.setHostnameVerifier(new TrustAllHost());
https.setSSLSocketFactory(getSocketFactory());
}
http.setRequestProperty("Content-Type", "application/soap+xml; charset=utf-8");
http.setRequestProperty("Content-Length", Integer.toString(message.size()));
http.setRequestProperty("SOAPAction", "");
http.setRequestProperty("Host", service.getHost());
//http.setRequestProperty("Proxy-Authorization", "Basic {proxy_auth}");
InputStream in = null;
OutputStream out = null;
try {
out = http.getOutputStream();
message.writeTo(out);
} finally {
if (out != null) {
out.flush();
out.close();
}
}
int responseCode = http.getResponseCode();
MimeHeaders responseHeaders = new MimeHeaders();
message.reset();
try {
in = http.getInputStream();
IOUtils.copy(in, message);
} catch (final IOException e) {
try {
in = http.getErrorStream();
IOUtils.copy(in, message);
} catch (IOException e1) {
throw new RuntimeException("Unable to read error body", e);
}
} finally {
if (in != null)
in.close();
}
for (Map.Entry<String, List<String>> header : http.getHeaderFields().entrySet()) {
String name = header.getKey();
if (name != null)
for (String value : header.getValue())
responseHeaders.addHeader(name, value);
}
SOAPMessage inMessage = MessageFactory.newInstance()
.createMessage(responseHeaders, new ByteArrayInputStream(message.toByteArray()));
if (inMessage == null)
throw new RuntimeException("Unable to read server response code " + responseCode);
msgCtx.setMessage(inMessage);
return false;
} catch (Exception e) {
throw new RuntimeException("Proxy error", e);
} finally {
if (http != null)
http.disconnect();
}
}
@Override public boolean handleFault(SOAPMessageContext context) {
return false;
}
@Override public void close(MessageContext context) {
}
@Override public Set<QName> getHeaders() {
return Collections.emptySet();
}
}
UrlConnectionを使用し、ハンドラーで必要なライブラリを使用できます。楽しんで!
トラストマネージャーをセットアップするときに、自己署名証明書を信頼するのに問題がありました。 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
メソッドの引数として渡します。