JAX-WS 클라이언트의 SSLContext를 프로그래밍 방식으로 설정하는 방법은 무엇입니까?
저는 브라우저 클라이언트가 있고 타사와의 서버 간 통신에도 참여하는 분산 응용 프로그램의 서버에서 작업하고 있습니다. 내 서버에는 클라이언트가 HTTP / S 및 XMPP (보안)를 사용하여 TLS (SSL) 통신을 사용하여 연결할 수 있도록 CA 서명 인증서가 있습니다. 모두 잘 작동합니다.
이제 HTTPS / SSL을 통해 JAX-WS를 사용하여 타사 서버에 안전하게 연결해야합니다. 이 통신에서 내 서버는 JAX-WS 상호 작용에서 클라이언트 역할을하며 제 3자가 서명 한 클라이언트 인증서가 있습니다.
표준 시스템 구성 ( -Djavax.net.ssl.keyStore=xyz
)을 통해 새 키 저장소를 추가하려고 했지만 다른 구성 요소가 이에 영향을받습니다. 내 다른 구성 요소는 SSL 구성 ( my.xmpp.keystore=xxx, my.xmpp.truststore=xxy, ...
)에 전용 매개 변수를 사용하고 있지만 결국 global SSLContext
. (구성 네임 스페이스 my.xmpp.
는 분리를 나타내는 것처럼 보였지만 그렇지 않습니다.)
또한 내 클라이언트 인증서를 원래 키 저장소에 추가하려고 시도했지만-다시-내 다른 구성 요소도 좋아하지 않는 것 같습니다.
남은 유일한 옵션은 JAX-WS HTTPS 구성에 프로그래밍 방식으로 연결하여 클라이언트 JAX-WS 상호 작용을위한 키 저장소 및 신뢰 저장소를 설정하는 것입니다.
이를 수행하는 방법에 대한 아이디어 / 포인터가 있습니까? 내가 찾은 모든 정보는 javax.net.ssl.keyStore
방법을 사용 하거나 SSLContext
동일한 confilc에서 끝날 것 같은 전역 을 설정하고 있습니다. 내가 가장 도움이되는 것은 필요한 기능을 요청하는이 오래된 버그 보고서였습니다 . SSLContext를 JAX-WS 클라이언트 런타임에 전달하기위한 지원 추가
테이크?
이것은 깨지기 어려운 너트 였으므로 기록을 위해 :
이를 해결하기 위해 사용자 정의 KeyManager
및 SSLSocketFactory
이 사용자 정의 KeyManager
를 사용하여 분리 된 KeyStore
. 나는이에 대한 기본 코드를 발견 KeyStore
하고 SSLFactory
이 우수 블로그 항목에 : 방법 - 투 - 동적으로 선택-A-인증서 별칭-때-호출-웹 서비스를
그런 다음 특수화 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을로드하는 경우 WSDL 수집을위한 HTTPS 요청이 사용하지 않으므로 부트 스트랩 문제가 발생합니다. 웹 서비스와 동일한 자격 증명. WSDL을 로컬에서 사용 가능하게 만들고 (file : /// ...) 웹 서비스 끝점을 동적으로 변경하여이 문제를 해결했습니다. (이것이 필요한 이유에 대한 좋은 토론 은이 포럼에서 찾을 수 있습니다. )
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, webServiceLocation);
이제 WebService가 부트 스트랩되고 이름이 지정된 (별칭) 클라이언트 인증서 및 상호 인증을 사용하여 SSL을 통해 서버 상대와 통신 할 수 있습니다. ∎
이것은 약간의 조정 으로이 게시물 을 기반으로 해결 한 방법 입니다. 이 솔루션은 추가 클래스를 생성 할 필요가 없습니다.
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
WSDL이 https : //로 액세스 가능하지 않으면 위의 내용은 괜찮습니다 (댓글에서 말했듯이).
이에 대한 해결 방법은 다음과 같습니다.
SSLSocketFactory를 기본값으로 설정하십시오.
HttpsURLConnection.setDefaultSSLSocketFactory(...);
내가 사용하는 Apache CXF의 경우 다음 줄을 구성에 추가해야합니다.
<http-conf:conduit name="*.http-conduit">
<http-conf:tlsClientParameters useHttpsURLConnectionDefaultSslSocketFactory="true" />
<http-conf:conduit>
시도했지만 여전히 작동하지 않는 사람들을 위해 이것은 Dynamic을 사용하여 Wildfly 8에서 나를 위해 해냈습니다 Dispatcher
.
bindingProvider.getRequestContext().put("com.sun.xml.ws.transport.https.client.SSLSocketFactory", yourSslSocketFactory);
internal
속성 키 의 부분은 여기에 없습니다.
신뢰 관리자를 설정할 때 자체 서명 된 인증서를 신뢰하는 데 문제가있었습니다. 아파치 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
.
여기에 단계를 시도했습니다.
http://jyotirbhandari.blogspot.com/2011/09/java-error-invalidalgorithmparameterexc.html
그리고 그것은 문제를 해결했습니다. 저는 약간의 조정을했습니다. System.getProperty를 사용하여 두 개의 매개 변수를 설정했습니다.
프록시 인증 및 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을 사용하며 핸들러에서 원하는 라이브러리를 사용할 수 있습니다. 즐기세요!
다음 접근 방식이 저에게 효과적이었습니다. 이 답변은 JAX-WS Axis2 구현을위한 것입니다. 나는 maasg의 대답에서 만들고 싶습니다. maasg에서 언급 한 것과 동일한 bootsratp 문제가 있었고 WSDL 파일을 로컬에서 사용할 수 있도록하여 문제를 해결했습니다. 그런 다음 SSL 컨텍스트를 제공 할 수 없었습니다.
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getCustomSocketFactory());
인터넷 검색을 많이 한 후이 블로그 에서 답변을 받았습니다 . 프로젝트에 Apache commons httpclient jar를 추가해야합니다.
Protocol authhttps = new Protocol ("https", new AuthSSLProtocolSocketFactory(new URL("file://location_to_your_keystore"), "keystorePassword", new URL("file://location_to_your_truststore"), "trustStorePassword"), 443);
Protocol.registerProtocol("https", authhttps);
감사.
'IT이야기' 카테고리의 다른 글
MongoDB에서 컬렉션의 이름 바꾸는 방법 (0) | 2021.03.27 |
---|---|
C ++에서 키 업데이트로 최소 우선 순위 대기열을 사용하는 가장 쉬운 방법 (0) | 2021.03.27 |
VB.NET에서 축 어적 문자열 리터럴을 수행하는 방법 (0) | 2021.03.27 |
Jackson : POJO를 수정하지 않고 JSON에 사용자 지정 속성을 추가하는 방법 (0) | 2021.03.27 |
Gradle : 앱에서 설정되는 플래그를 사용하여 Android 라이브러리에서 BuildConfig를 사용하는 방법 (0) | 2021.03.26 |