I configure HttpsUrlConnection like this:
HttpsURLConnection.setDefaultSSLSocketFactory(sslFactory);
HttpsURLConnection.setDefaultHostnameVerifier(new DummyHostnameVerifier());
DummyHostnameVerifier:
public class DummyHostnameVerifier implements HostnameVerifier {
#Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
}
Of course, it's only part of configuration. But the problem is that verify method in DummyHostnameVerifier isn't invoked.
When I test my application on local machine, glassfish 3 server, verify invoked and I'm not recieving any exceptions.
But when I test it on remote environment, verify isn't invoked, and I recieve this:
java.io.IOException: The https URL hostname does not match the Common Name (CN) on the server certificate. To disable this check (NOT recommended for production) set the CXF client TLS configuration property "disableCNCheck" to true.
On remote env app runs on jboss 5.
Maybe this depends on some jboss config? I can't understand, where default hostname verifier changed after setting my verifier.
I think if you want to by pass the certificateValidation you would need to create Trustmanager which will not go for certificate validation
HttpsURLConnection.setDefaultHostnameVerifier(new DummyHostnameVerifier());
// Create a TrustManager which wont validate certificate chains start
javax.net.ssl.TrustManager[] trustAllCertificates = new javax.net.ssl.TrustManager[1];
javax.net.ssl.TrustManager tm = new miTM();
trustAllCertificates[0] = tm;
javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("SSL");
sc.init(null, trustAllCertificates, null);
// Create a TrustManager which wont validate certificate chains end
HttpsURLConnection.setDefaultSSLSocketFactory(sslFactory);
Could you please try with above code and let me know if you get the resolution ?
The problem was in following: somehow there wasn't action name in message to server.
I configured connection like this:
HttpsURLConnection.setDefaultSSLSocketFactory(sslFactory);
HttpsURLConnection.setDefaultHostnameVerifier(new DummyHostnameVerifier());
URL url = null;
try {
url = new URL(endpoint + "/wsdl");
} catch (MalformedURLException e) {
LOG.error(e.getMessage());
}
javax.xml.ws.Service s = MyService.create(url, new QName(MyService.NAMESPACE, MyService.SERVICE));
ServiceSoap port = s.getPort(ServiceSoap.class);
Map<String, Object> reqCtx = ((BindingProvider)port).getRequestContext();
reqCtx.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint);
reqCtx.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE);
reqCtx.put(BindingProvider.SOAPACTION_URI_PROPERTY, actionName);
Client client = ClientProxy.getClient(port);
HTTPConduit http = (HTTPConduit) client.getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnection(ConnectionType.CLOSE);
http.setClient(httpClientPolicy);
TLSClientParameters tls = new TLSClientParameters();
tls.setSSLSocketFactory(sslFactory);
tls.setDisableCNCheck(true);
http.setTlsClientParameters(tls);
So, port configured and everything began to work.
Related
I'm programming a JAX-WS client in Java. The access to the WebService is protected with a client certificate. I know the client certificate is right because I can get the WSDL in Firefox only if the client certificate was imported (in Firefox).
But I have problems to write my java application which should use the WebService. What I have do is following:
MyOwnService svc = new MyOwnService(getServerURL(), MYOWNSERVICE_QNAME);
...
...
private URL getServerURL() throws IOException {
URL url = new URL((String) cfg.get(ConfigData.SERVER_URL));
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
try {
con.setSSLSocketFactory(getFactory(new File("/etc/pki/wildfly/client.keystore"), "123456"));
} catch (Exception exc) {
throw new IOException("Client certificate error!", exc);
}
return url;
}
private SSLSocketFactory getFactory(File pKeyFile, String pKeyPassword )
throws ... {
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream keyInput = new FileInputStream(pKeyFile);
keyStore.load(keyInput, pKeyPassword.toCharArray());
keyInput.close();
keyManagerFactory.init(keyStore, pKeyPassword.toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
return context.getSocketFactory();
}
But this didn't work. If I run this I get following exception in the MyOwnService constructor
java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty.
How is the correct way to implemnting a JAX-WS client which supports client certification?
Forget my code above. Seem's that the only thing you must do is to specify the keystore as environment variables like:
-Djavax.net.ssl.keyStore=/etc/pki/wildfly/client.keystore -Djavax.net.ssl.keyStorePassword=123456
If I do so and specifying the correct keystore it works. If I specify an invalid keystore file (which contains a other/wrong certificate/key) it don't works :) .
But I'm not sure how Java gets the right key/cert from the keystore if the keystore contains more as one PrivateKeyEntry. Specifying javax.net.ssl.keyStoreAlias will be have no effect. May be Java tries the PrivateKeyEntrys until the right one was found ...
However: The only thing to do is to specify the right keystore as environment variable.
I'm working on a server in a distributed application that has browser clients and also participates in server-to-server communication with a 3rd party.
My server has a CA-signed certificate to let my clients connect using TLS (SSL) communication using HTTP/S and XMPP(secure). That's all working fine.
Now I need to securely connect to a 3rd party server using JAX-WS over HTTPS/SSL. In this communication, my server acts as client in the JAX-WS interation and I've a client certificate signed by the 3rd party.
I tried adding a new keystore through the standard system configuration (-Djavax.net.ssl.keyStore=xyz) but my other components are clearly affected by this. Although my other components are using dedicated parameters for their SSL configuration (my.xmpp.keystore=xxx, my.xmpp.truststore=xxy, ...), it seems that they end up using the global SSLContext. (The configuration namespace my.xmpp. seemed to indicate separation, but it's not the case)
I also tried adding my client certificate into my original keystore, but -again- my other components don't seem to like it either.
I think that my only option left is to programmatically hook into the JAX-WS HTTPS configuration to setup the keystore and truststore for the client JAX-WS interaction.
Any ideas/pointers on how to do this? All information I find either uses the javax.net.ssl.keyStore method or is setting the global SSLContext that -I guess- will end up in the same confilc. The closest I got to something helpful was this old bug report that requests the feature I need: Add support for passing an SSLContext to the JAX-WS client runtime
Any takes?
This one was a hard nut to crack, so for the record:
To solve this, it required a custom KeyManager and a SSLSocketFactory that uses this custom KeyManager to access the separated KeyStore.
I found the base code for this KeyStore and SSLFactory on this excellent blog entry:
how-to-dynamically-select-a-certificate-alias-when-invoking-web-services
Then, the specialized SSLSocketFactory needs to be inserted into the WebService context:
service = getWebServicePort(getWSDLLocation());
BindingProvider bindingProvider = (BindingProvider) service;
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getCustomSocketFactory());
Where the getCustomSocketFactory() returns a SSLSocketFactory created using the method mentioned above. This would only work for JAX-WS RI from the Sun-Oracle impl built into the JDK, given that the string indicating the SSLSocketFactory property is proprietary for this implementation.
At this stage, the JAX-WS service communication is secured through SSL, but if you are loading the WSDL from the same secure server () then you'll have a bootstrap problem, as the HTTPS request to gather the WSDL will not be using the same credentials than the Web Service. I worked around this problem by making the WSDL locally available (file:///...) and dynamically changing the web service endpoint: (a good discussion on why this is needed can be found in this forum)
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, webServiceLocation);
Now the WebService gets bootstrapped and is able to communicate through SSL with the server counterpart using a named (alias) Client-Certificate and mutual authentication. ∎
This is how I solved it based on this post with some minor tweaks. This solution does not require creation of any additional classes.
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() );
I tried the following and it didn't work on my environment:
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getCustomSocketFactory());
But different property worked like a charm:
bindingProvider.getRequestContext().put(JAXWSProperties.SSL_SOCKET_FACTORY, getCustomSocketFactory());
The rest of the code was taken from the first reply.
By combining Radek and l0co's answers you can access the WSDL behind https:
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
The above is fine (as I said in comment) unless your WSDL is accessible with https:// too.
Here is my workaround for this:
Set you SSLSocketFactory as default:
HttpsURLConnection.setDefaultSSLSocketFactory(...);
For Apache CXF which I use you need also add these lines to your config:
<http-conf:conduit name="*.http-conduit">
<http-conf:tlsClientParameters useHttpsURLConnectionDefaultSslSocketFactory="true" />
<http-conf:conduit>
You can move your proxy authentication and ssl staff to soap handler
port = new SomeService().getServicePort();
Binding binding = ((BindingProvider) port).getBinding();
binding.setHandlerChain(Collections.<Handler>singletonList(new ProxyHandler()));
This is my example, do all network ops
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();
}
}
It use UrlConnection, you can use any library you want in handler.
Have fun!
For those trying and still not getting it to work, this did it for me with Wildfly 8, using the dynamic Dispatcher:
bindingProvider.getRequestContext().put("com.sun.xml.ws.transport.https.client.SSLSocketFactory", yourSslSocketFactory);
Note that the internal part from the Property key is gone here.
I had problems trusting a self signed certificate when setting up the trust manager. I used the SSLContexts builder of the apache httpclient to create a custom 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);
and passing in the new TrustSelfSignedStrategy() as an argument in the loadTrustMaterial method.
we faced this problem, due to a keystore clash between system integrations, so we used the following code.
private PerSecurityWS prepareConnectionPort() {
final String HOST_BUNDLE_SYMBOLIC_NAME = "wpp.ibm.dailyexchangerates";
final String PATH_TO_SLL = "ssl/<your p.12 certificate>";
final File ksFile = getFile(HOST_BUNDLE_SYMBOLIC_NAME, PATH_TO_SLL);
final String serverURI = "you url";
final KeyStore keyStore = KeyStore.getInstance("pkcs12");
keyStore.load(new FileInputStream(ksFile.getAbsolutePath()), keyStorePassword.toCharArray());
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyStorePassword.toCharArray());
final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
#Override
public boolean verify(final String hostname, final SSLSession session) {
return false;
}
};
final SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), null, null);
final SSLSocketFactory sslSocketFactory = ctx.getSocketFactory();
final PerSecurityWS port = new PerSecurityWS_Service().getPerSecurityWSPort();
final BindingProvider bindingProvider = (BindingProvider) port;
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory",sslSocketFactory);
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, serverURI);
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.hostname.verifier",DO_NOT_VERIFY);
return port;
}
I tried the steps here:
http://jyotirbhandari.blogspot.com/2011/09/java-error-invalidalgorithmparameterexc.html
And, that fixed the issue. I made some minor tweaks - I set the two parameters using System.getProperty...
I'm using version 4.2.5. of AutoRetryHttpClient from org.apache.httpcomponents to download a pdf file from an url whose scheme is https. The code is written in NetBeans 7.3 and uses JDK7.
Supposing that the imaginary pdf resource is at https://www.thedomain.with/my_resource.pdf, then I have the following code:
SchemeRegistry registry = new SchemeRegistry();
try {
final SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy() {
#Override
public boolean isTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
return true;
}
});
registry.register(new Scheme("https", 3920, sf));
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | UnrecoverableKeyException ex) {
Logger.getLogger(HttpConnection.class.getName()).log(Level.SEVERE, null, ex);
}
//Here I create the client.
HttpClient client = new AutoRetryHttpClient(new DefaultHttpClient(new PoolingClientConnectionManager(registry)),
new DefaultServiceUnavailableRetryStrategy(5, //num of max retries
100//retry interval));
HttpResponse httpResponse = null;
try {
HttpGet httpget = new HttpGet("https://www.thedomain.with/my_resource.pdf");
//I set header and Mozilla User-Agent
httpResponse = client.execute(httpget);
} catch (IOException ex) {
}
... //other lines of code to get and save the file, not really important since the code is never reached
When I call client.execute the following exception is thrown
org.apache.http.conn.HttpHostConnectException: Connection to https://www.thedomain.with refused
What can I do to get that pdf resource?
PS: I can download it via browser, so exists a way to obtain that file.
There seem to be a couple of problems:
You registered the Scheme to use 3920 as the default port, which is a non-standard port number for HTTPS. If the server is actually running on that port, then you would have to access using this URL in the browser: https://www.thedomain.with:3920/my_resource.pdf. Since the URL that you use in the browser does not include the 3920 port, then the server will be running on the default port of 443, so you should use change new Scheme("https", 3920, sf) to new Scheme("https", 443, sf).
It appears that the CN in your server's certificate doesn't match its hostname, which is causing the SSLPeerUnverifiedException. In order for this to work, you would need to use the SSLSocketFactory(TrustStrategy, HostnameVerifier) constructor and pass a verifier that doesn't do this check. Apache provides the AllowAllHostnameVerifier for this purpose.
Note: You really shouldn't use the no-op TrustStrategy and HostnameVerifier in production code, as this essentially turns off all security checks in terms of authenticating the remote server and leaves you open to impersonation attacks.
I'm writing a standalone java app.
I have to use default system (Windows XP, 7) proxy settings to call the SOAP web-services using HTTPS protocol.
To determine system proxy settings I use the ProxySelector class. Here's the code:
System.setProperty("java.net.useSystemProxies", "true");
List proxies = ProxySelector.getDefault().select(new URI("https://www.google.com/"));
for (Iterator iter = proxies.iterator(); iter.hasNext();) {
Proxy proxy = (Proxy) iter.next();
InetSocketAddress address = (InetSocketAddress) proxy.address();
if (address == null) {
UTLogger.getLogger().debug("No proxy (direct connection)");
} else {
if (proxy.type().equals(Proxy.Type.HTTP)) {
System.setProperty("http.proxyHost", address.getHostName());
System.setProperty("http.proxyPort", String.valueOf(address.getPort()));
System.setProperty("https.proxyHost", address.getHostName());
System.setProperty("https.proxyPort", String.valueOf(address.getPort()));
} else if (proxy.type().equals(Proxy.Type.SOCKS)) {
System.setProperty("socksProxyHost", address.getHostName());
System.setProperty("socksProxyPort", String.valueOf(address.getPort()));
}
}
}
System.setProperty("java.net.useSystemProxies", "false");
And here's how I initialize connection:
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[]{new TrustDummy()}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
servicePort = new MyPort(new URL(wsdlLocation), new QName(nameSpaceURI, localPart)).getMyPortSoap();
I don't understand why there is no Proxy.Type.HTTPS? If i call a web-service method i get the following errors.
With http proxy set:
javax.xml.ws.WebServiceException: Failed to access the WSDL at: ht tps://...?wsdl. It failed with: Got Unrecognized SSL message, plaintext connection? while opening stream from ht tps://...?wsdl.
and with https proxy set:
javax.xml.ws.WebServiceException: Failed to access the WSDL at: ht tps://...?wsdl. It failed with: Got Unable to tunnel through proxy. Proxy returns "HTTP/1.0 403 Forbidden" while opening stream from ht tps://...?wsdl.
Am I doing it wrong?
I'm working on a server in a distributed application that has browser clients and also participates in server-to-server communication with a 3rd party.
My server has a CA-signed certificate to let my clients connect using TLS (SSL) communication using HTTP/S and XMPP(secure). That's all working fine.
Now I need to securely connect to a 3rd party server using JAX-WS over HTTPS/SSL. In this communication, my server acts as client in the JAX-WS interation and I've a client certificate signed by the 3rd party.
I tried adding a new keystore through the standard system configuration (-Djavax.net.ssl.keyStore=xyz) but my other components are clearly affected by this. Although my other components are using dedicated parameters for their SSL configuration (my.xmpp.keystore=xxx, my.xmpp.truststore=xxy, ...), it seems that they end up using the global SSLContext. (The configuration namespace my.xmpp. seemed to indicate separation, but it's not the case)
I also tried adding my client certificate into my original keystore, but -again- my other components don't seem to like it either.
I think that my only option left is to programmatically hook into the JAX-WS HTTPS configuration to setup the keystore and truststore for the client JAX-WS interaction.
Any ideas/pointers on how to do this? All information I find either uses the javax.net.ssl.keyStore method or is setting the global SSLContext that -I guess- will end up in the same confilc. The closest I got to something helpful was this old bug report that requests the feature I need: Add support for passing an SSLContext to the JAX-WS client runtime
Any takes?
This one was a hard nut to crack, so for the record:
To solve this, it required a custom KeyManager and a SSLSocketFactory that uses this custom KeyManager to access the separated KeyStore.
I found the base code for this KeyStore and SSLFactory on this excellent blog entry:
how-to-dynamically-select-a-certificate-alias-when-invoking-web-services
Then, the specialized SSLSocketFactory needs to be inserted into the WebService context:
service = getWebServicePort(getWSDLLocation());
BindingProvider bindingProvider = (BindingProvider) service;
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getCustomSocketFactory());
Where the getCustomSocketFactory() returns a SSLSocketFactory created using the method mentioned above. This would only work for JAX-WS RI from the Sun-Oracle impl built into the JDK, given that the string indicating the SSLSocketFactory property is proprietary for this implementation.
At this stage, the JAX-WS service communication is secured through SSL, but if you are loading the WSDL from the same secure server () then you'll have a bootstrap problem, as the HTTPS request to gather the WSDL will not be using the same credentials than the Web Service. I worked around this problem by making the WSDL locally available (file:///...) and dynamically changing the web service endpoint: (a good discussion on why this is needed can be found in this forum)
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, webServiceLocation);
Now the WebService gets bootstrapped and is able to communicate through SSL with the server counterpart using a named (alias) Client-Certificate and mutual authentication. ∎
This is how I solved it based on this post with some minor tweaks. This solution does not require creation of any additional classes.
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() );
I tried the following and it didn't work on my environment:
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getCustomSocketFactory());
But different property worked like a charm:
bindingProvider.getRequestContext().put(JAXWSProperties.SSL_SOCKET_FACTORY, getCustomSocketFactory());
The rest of the code was taken from the first reply.
By combining Radek and l0co's answers you can access the WSDL behind https:
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
The above is fine (as I said in comment) unless your WSDL is accessible with https:// too.
Here is my workaround for this:
Set you SSLSocketFactory as default:
HttpsURLConnection.setDefaultSSLSocketFactory(...);
For Apache CXF which I use you need also add these lines to your config:
<http-conf:conduit name="*.http-conduit">
<http-conf:tlsClientParameters useHttpsURLConnectionDefaultSslSocketFactory="true" />
<http-conf:conduit>
You can move your proxy authentication and ssl staff to soap handler
port = new SomeService().getServicePort();
Binding binding = ((BindingProvider) port).getBinding();
binding.setHandlerChain(Collections.<Handler>singletonList(new ProxyHandler()));
This is my example, do all network ops
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();
}
}
It use UrlConnection, you can use any library you want in handler.
Have fun!
For those trying and still not getting it to work, this did it for me with Wildfly 8, using the dynamic Dispatcher:
bindingProvider.getRequestContext().put("com.sun.xml.ws.transport.https.client.SSLSocketFactory", yourSslSocketFactory);
Note that the internal part from the Property key is gone here.
I had problems trusting a self signed certificate when setting up the trust manager. I used the SSLContexts builder of the apache httpclient to create a custom 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);
and passing in the new TrustSelfSignedStrategy() as an argument in the loadTrustMaterial method.
we faced this problem, due to a keystore clash between system integrations, so we used the following code.
private PerSecurityWS prepareConnectionPort() {
final String HOST_BUNDLE_SYMBOLIC_NAME = "wpp.ibm.dailyexchangerates";
final String PATH_TO_SLL = "ssl/<your p.12 certificate>";
final File ksFile = getFile(HOST_BUNDLE_SYMBOLIC_NAME, PATH_TO_SLL);
final String serverURI = "you url";
final KeyStore keyStore = KeyStore.getInstance("pkcs12");
keyStore.load(new FileInputStream(ksFile.getAbsolutePath()), keyStorePassword.toCharArray());
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyStorePassword.toCharArray());
final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
#Override
public boolean verify(final String hostname, final SSLSession session) {
return false;
}
};
final SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), null, null);
final SSLSocketFactory sslSocketFactory = ctx.getSocketFactory();
final PerSecurityWS port = new PerSecurityWS_Service().getPerSecurityWSPort();
final BindingProvider bindingProvider = (BindingProvider) port;
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory",sslSocketFactory);
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, serverURI);
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.hostname.verifier",DO_NOT_VERIFY);
return port;
}
I tried the steps here:
http://jyotirbhandari.blogspot.com/2011/09/java-error-invalidalgorithmparameterexc.html
And, that fixed the issue. I made some minor tweaks - I set the two parameters using System.getProperty...