I am working on a Java application, trying to retrieve the password from a Cyberark Vault using Rest API call. I get the data back for the API call through the browser (Edge/Chrome)after importing the client certificate. I tried adding the same cert into java truststore "C:\jdk1.8.0_77\jre\lib\security\cacerts" but when making the call, I get the below error
403 - Forbidden: Access is denied. You do not have permission to view
this directory or page using the credentials that you supplied.
I used to get this error in the browser as well until I imported the client cert. So what am I missing now? Are there any attributes/variables that needs to be set to make the call? I am using Apache HttpClient. I am passing the truststore, password as VM arguments in Eclipse.
KeyStore keyStore = null;
String baseUrl = "https://cyberarkservices:23456/api/Accounts?AppID=myapp&Safe=Test&Object=testobject";
try {
keyStore = KeyStore.getInstance("JKS");
} catch (KeyStoreException e) {
System.out.println(e.getStackTrace());
}
FileInputStream instream = null;
try {
instream = new FileInputStream(new File(System.getProperty("javax.net.ssl.trustStore")));
keyStore.load(instream, System.getProperty("javax.net.ssl.trustStorePassword").toCharArray());
} catch (Exception e) {
System.out.println("Exception occured loading cacerts: " + e);
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = null;
try {
sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, System.getProperty("javax.net.ssl.trustStorePassword").toCharArray())
.build();
} catch (KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e) {
System.out.println("Exception occured loading SSL key material: " + e);
}
HttpClientBuilder builder = HttpClientBuilder.create();
SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(sslcontext,
new String[] { "TLSv1.1", "TLSv1.2" }, null,
NoopHostnameVerifier.INSTANCE);
builder.setSSLSocketFactory(sslConnectionFactory);
builder.setSSLSocketFactory(sslConnectionFactory);
CloseableHttpClient httpclient = builder.build();
CloseableHttpResponse response = null;
try {
HttpGet httpget = new HttpGet(baseUrl);
// CALL API
String reply = "";
response = httpclient.execute(httpget);
String res_xml = EntityUtils.toString(response.getEntity());
if(res_xml!=null && !res_xml.isEmpty())
{
reply = res_xml;
System.out.println(reply);
}
I got it to work. Just read in/loaded the client certificate(.p12) directly from java while making the API call without importing it into trust store or keystore file. Passed in the cert location/password as VM arguments and it worked just fine.
Related
I am building an adapter application that should sent the data to an external service using HTTPS. While testing locally (against a mock server), I am saving the certificate in a local truststore and loading it using loadTrustMaterial(file, password) method. The difference in productive environment is that the application is running in OpenShift and the certificate is saved in ConfigMap.
Question:
How can I retrieve that certificate from ConfigMap in OpenShift (is there any url to the cert?), so my application can call external service?
Here are some code snippets for clarity:
private HttpResponse connectToExternalService(String xml) {
StringEntity stringEntity = null;
try {
stringEntity = new StringEntity(xml);
SSLContext sslContext = this.createSSLContext();
SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslContext);
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(factory).build();
HttpPost post = new HttpPost(url);
post.setEntity(stringEntity);
post.setHeader("Content-Type", "text/xml");
return client.execute(post);
} catch (IOException e) {
}
}
private SSLContext createSSLContext() {
File file = new File("path to the truststore");
try {
char[] password = "password".toCharArray();
return SSLContexts.custom().loadTrustMaterial(file, password).build();
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
}
}
As config map being used to hold the certificate, the same can be mounted as file inside the container. Please refer k8s documentation to load as file. Post that add it trust store during container start.
I am currently working on a webservice that uses http! I have been asked to change (to use ) https instead to call this webservice!
I am using eclipse kepler and JBoss EAP6.1
I found in the internet that I have to create a keystore and edit the server.xml file.
The thing is that i can't find the xml file in this JBOss version [ i have a standalone.xml file is it the same ? ]
and for the generation of the keystore where do i have to do it ?
Thank you for you ansewers!
if I am on the wrong way, would you please re-direct me to right path ?
Thanks again !
Get the certificate of the HTTPS url. (You can do it by typing the URL in the browser and then extracting the certificate from the browser certificate installation location). After this add this certificate to the JRE of your application which is used by JBOSS server. Most probably this will be the JRE you have given in the system environment. You can google to get how to install certificate in the keystore. May be this will work.
You're calling a remote webservice via https, right?
Ok, you could import the certificate of the remote service in the keystore (plenty of guides about that, look at this other question for an example)
OR
You can bypass the whole https certificate thing (launch this static method before the remote call):
/**
* Bypassing SSL certificate check
*
* #throws Exception
*/
public static void doTrustToCertificates() throws Exception {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HostnameVerifier hv = new HostnameVerifier() {
#Override
public boolean verify(String urlHostName, SSLSession session) {
if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
logger.warn("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
}
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
In addition to answer of #Rahul, you can import certificate (.cer) file using following command on command prompt for windows OS :
(Assuming you have set required Java paths)
keytool -importcert -file <path of certificate>\<YourCertificateName>.cer -keystore D:\java\jdk1.7.0_40\jre\lib\security\cacerts -alias <certificateAliasName> -storepass <Password>
usually default <password> is 'changeit'.
In case webservice is used for third party client then you can use HttpClient to interact. I am not sure what kind of operation you are performing with that webservice. I assume you want to send some xml to that URL. You can refer following code :
HttpPost httppost = new HttpPost(url);
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(username, password));
CloseableHttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(credentialsProvider).build();
StringEntity entity = null;
try {
entity = new StringEntity(xmlToSend);
} catch (UnsupportedEncodingException e) {
LOG.error("Unsupported Encoding ", e);
}
entity.setContentType("text/xml");
httppost.setEntity(entity);
try{
CloseableHttpResponse response = client.execute(httppost);
returnCode = response.getStatusLine().getStatusCode();
EntityUtils.consume(entity);
LOG.debug("HttpResponse :" + EntityUtils.toString(response.getEntity()));
}catch(IOException e){
LOG.error("Error occured while sending the xml");
}
I am building an application server in Java using the HttpServer class. I have this server functioning perfectly using plain text communication over HTTP. However, I wish to upgrade this to using SSL, using the HttpsServer class.
I used this question as a basis to work from: Simple Java HTTPS server
My server class is as follows:
public Server(Options options){
SSLContext sslContext = null;
try {
server = HttpsServer.create(new InetSocketAddress(8080), 0);
sslContext = SSLContext.getInstance("TLS");
char[] password = options.getSSLPassword().toCharArray();
KeyStore ks = KeyStore.getInstance ("JKS");
FileInputStream fis = new FileInputStream (options.getSSLKeystore());
ks.load ( fis, password );
KeyManagerFactory kmf = KeyManagerFactory.getInstance ( "SunX509" );
kmf.init ( ks, password );
TrustManagerFactory tmf = TrustManagerFactory.getInstance ( "SunX509" );
tmf.init ( ks );
sslContext.init ( kmf.getKeyManagers (), tmf.getTrustManagers (), null );
} catch (Exception e) {
e.printStackTrace();
}
HttpsConfigurator httpsConfigurator = new HttpsConfigurator(sslContext) {
#Override
public void configure(HttpsParameters httpsParameters) {
SSLContext sslContext = getSSLContext();
SSLParameters defaultSSLParameters = sslContext.getDefaultSSLParameters();
httpsParameters.setSSLParameters(defaultSSLParameters);
}
};
server.createContext("/", new HttpHandler() {
#Override
public void handle(HttpExchange t) throws IOException {
HttpsExchange s = (HttpsExchange)t;
s.getSSLSession();
String response = "<html><body>Hello world.</body></html>";
t.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
});
server.setExecutor(Executors.newCachedThreadPool());
System.out.println("Starting server on port " + port + "...");
server.setHttpsConfigurator(httpsConfigurator);
server.start();
System.out.println("Server started successfully!");
}
This compiles and runs fine, but then when I try to connect to through a browser on localhost:8080 I get "no data received" and on https://localhost:8080 I get "webpage is not available"
There are no exceptions being thrown and it seems to run with no issues, apart from the fact that it just does nothing.
I used the keytool program to generate the keystore, however I am unfamiliar with this process so perhaps this is incorrect? But again, it accepts this as it is setting up the keystore and keyManagers etc.
Do I need to change my HttpHandler or contexts to handle an SSL exchange or something?
I have been able to get the program working with SSL with the code I provided in my question. I believe the issue I was having was because of the keystore I had generated. Using this command to generate the keystore it worked:
keytool -genkey -alias alias -keyalg RSA -keystore keystore.jks -keysize 2048
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 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...