How to disable Host name verification for nimbus JWKS ResourceRetriever - java

First I was using 'DefaultResourceRetriever' without any configuration like this :
new DefaultResourceRetriever(1000, 1000);
and then I got the following exception
java.security.cert.CertificateException: No subject alternative DNS name matching my-jwks-url.com found.
To by pass certificate check I have configured the resource retriever like below;
TrustStrategy trustStrategy = (X509Certificate[] x509Certificates, String s) -> true;
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(null, trustStrategy)
.build();
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
return new DefaultResourceRetriever(1000, 1000, 0, true, socketFactory);
But it doesn't changed anything.
I could set Hostname verifier to SSLConnectionSocketFactory like this:
new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier()) But nimbus ResourceRetriever only accept SSLSocketFactory as a parameter.
Is there any way to disable Hostname verification?

I resolved it by extending DefaultResourceRetriever and overriding openConnection(URL url) method.
If URL is HTTPS, it creates HttpsURLConnection. And we can set NoopHostnameVerifier to it.
Here is my solution :
public class NoopHostnameVerifyingResourceRetriever extends DefaultResourceRetriever {
public NoopHostnameVerifyingResourceRetriever(int connectTimeout, int readTimeout) {
super(connectTimeout, readTimeout);
}
#Override
protected HttpURLConnection openConnection(URL url) throws IOException {
HttpURLConnection connection = super.openConnection(url);
if (connection instanceof HttpsURLConnection) {
((HttpsURLConnection) connection).setHostnameVerifier(new NoopHostnameVerifier());
}
return connection;
}
}

Related

Impossible to start multiple HTTPS connections from Spring application

I have a Spring Boot application that tries to open a javax.net.ssl.HttpsURLConnection to a server but the response received is: java.io.IOException: Server returned HTTP response code: 403 for URL: https://serverIP:8443/path
When the keyStore, trustStore and their passwords are set as system properties the request works correctly and the expected JSON response is received:
System.setProperty("javax.net.ssl.keyStore", "src/main/resources/myKeyStore.p12");
System.setProperty("javax.net.ssl.trustStore", "src/main/resources/myTrustStore.truststore");
System.setProperty("javax.net.ssl.keyStorePassword", "myPassword");
System.setProperty("javax.net.ssl.trustStorePassword", "myPassword");
But the 403 response code is received when trying to set the information in SSLContext, instead of setting the system properties, by using this method that returns an SSLContext object:
public static SSLContext getSslContext(String trustStoreFile, String keystoreFile, String password)
throws GeneralSecurityException, IOException {
final KeyStore keystore = KeyStore.getInstance("pkcs12"); // also tried with JKS
try (final InputStream inKeystore = new FileInputStream(keystoreFile)) {
keystore.load(inKeystore, password.toCharArray());
}
try (final InputStream inTruststore = new FileInputStream(trustStoreFile)) {
keystore.load(inTruststore, password.toCharArray());
}
final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX"); // also tried with .getDefaultAlgorithm()
keyManagerFactory.init(keystore, password.toCharArray());
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keystore);
X509TrustManager x509Tm = null;
for (final TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
if (trustManager instanceof X509TrustManager) {
x509Tm = (X509TrustManager) trustManager;
break;
}
}
final X509TrustManager finalTm = x509Tm;
final X509ExtendedTrustManager customTm = new X509ExtendedTrustManager() {
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return finalTm.getAcceptedIssuers();
}
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException {
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException {
}
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {
}
};
final SSLContext sslContext = SSLContext.getInstance("TLS"); // also tried with SSL
sslContext.init(
keyManagerFactory.getKeyManagers(),
new TrustManager[]{customTm},
new SecureRandom());
final HostnameVerifier allHostsValid = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
return sslContext;
}
OBS: The trustStore and keyStore have the same password, that's why the method has only one password parameter and used for both key and trust manager factories.
The way the getSslContext method is called and used is:
final SSLContext sslContext = SSLContextHelper.getSslContext("src/main/resources/myTrustStore.truststore",
"src/main/resources/myKeyStore.p12",
"myPassword");
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
final URL url = new URL("https://serverIP:8443/path");
final HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(sslSocketFactory);
// tried adding some headers to the request
urlConnection.addRequestProperty("Content-Type", "application/json");
urlConnection.addRequestProperty("Accept", "application/json");
urlConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0");
urlConnection.connect();
final InputStream inputstream = urlConnection.getInputStream();
The error is thrown at the last line when trying to get the inputStream of the URL connection.
Also, I tried using the following classes from org.apache.http: SSLConnectionSocketFactory, HttpClient, HttpGet, HttpResponse but response code is still 403.
I can only think that there is something missing from the SSL configuration because the system properties work. Any suggestions on what I miss setting in the SSLContext/SSLSocketFactory or how can I solve/better debug the problem are welcome! Thanks!
I managed to open the HTTPS connections only by using Spring's RestTemplate (org.springframework.web.client.RestTemplate) that uses the org.apache.http.client.HttpClient.
The method for getting the RestTemplate that has in its SSLContext the keyStore, trustStore and their passwords is the following:
public RestTemplate getRestTemplate(final String keyStoreFile, final String trustStoreFile,
final String password) throws Exception {
final SSLContext sslContext = SSLContextBuilder.create()
.loadKeyMaterial(ResourceUtils.getFile(keyStoreFile), password.toCharArray(), password.toCharArray())
.loadTrustMaterial(ResourceUtils.getFile(trustStoreFile), password.toCharArray())
.build();
final HttpClient client = HttpClients.custom()
.setSSLContext(sslContext)
.build();
final HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpComponentsClientHttpRequestFactory.setHttpClient(client);
return new RestTemplate(httpComponentsClientHttpRequestFactory);
}
The way that the RestTemplate is used for the HTTPS call is:
final String keyStoreFile = "src/main/resources/myKeyStore.p12";
final String trustStoreFile = "src/main/resources/myTrustStore.truststore";
final String password = "myPassword"; // same password for keyStore and trustStore
final String response = getRestTemplate(keyStoreFile, trustStoreFile, password).getForObject("https://serverIP:8443/path", String.class);
LOGGER.info("Response received: " + response);
Hope this helps anyone, had a lot of struggle with the HTTPS connections :)

Change keystore and truststore at runtime [duplicate]

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...

Access denied to setFactory by executing HttpsURLConnection.setDefaultSSLSocketFactory

My scope is to send an https message trusting all certificates from a Java application on an Embedded Linux Module with a JVM (IcedTea6 1.11, Java Version 1.6.0_24) on it (as Info I have no access to this Embedded Linux or to the JVM to make any changes, I can only put compiled java application on it).
I have only basic Java knowledge but I wrote from some examples found on the net an application that trust all certificates.
At the beginning I had an error on the line
SSLContext sc = SSLContext.getInstance("SSL");
and I found out that the problem was that the SunJSSE Provider was not implemented in the JVM.
My first step was to add the SunJSSE Provider
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
and after this if I read out the providers I can see that the adding was successfully and now I find the provider “SunJSSE version 1.6”.
The Trust all Certificate Class is as following:
public final class TrustAllCertificates implements X509TrustManager, HostnameVerifier
{
public X509Certificate[] getAcceptedIssuers() {return null;}
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
public boolean verify(String hostname, SSLSession session) {return true;}
public static void install()
{
try
{
TrustAllCertificates trustAll = new TrustAllCertificates();
final SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[]{trustAll}, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(trustAll);
}
catch (Exeption e)
{
JatLog.writeTempLog("Error: " + e.getMessage());
}
}
}
Now I receive always the error
access denied (java.lang.RuntimePermission setFactory)
On executing the line
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
Someone has any idea about how to solve this?
looks like the SecurityManager blocks this. Try settings for a single connection:
SSLContext sc = SSLContext.getInstance("SSL");
TrustAllCertificates trustAll = new TrustAllCertificates();
sc.init(null, new TrustManager[] { trustAll }, new java.security.SecureRandom());
URL url = new URL("https://www.google.com/");
URLConnection urlConnection = url.openConnection();
if (urlConnection instanceof HttpsURLConnection) {
HttpsURLConnection uc = (HttpsURLConnection) urlConnection;
uc.setSSLSocketFactory(sc.getSocketFactory());
uc.setHostnameVerifier(trustAll);
uc.connect();
JatLog.writeTempLog("headers: "+uc.getHeaderFields());
uc.disconnect();
}
if that doesnt help ask the swedish guys to update security manager settings ;)
hth

How to programmatically set the SSLContext of a JAX-WS client?

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...

How to handle invalid SSL certificates with Apache HttpClient? [duplicate]

This question already has answers here:
Resolving javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed Error?
(33 answers)
Closed 3 years ago.
I know, there are many different questions and so many answers about this problem... But I can't understand...
I have: ubuntu-9.10-desktop-amd64 + NetBeans6.7.1 installed "as is" from off. rep.
I need connecting to some site over the HTTPS. For this I use Apache's HttpClient.
From tutorial I read:
"Once you have JSSE correctly installed, secure HTTP communication over SSL should be as
simple as plain HTTP communication." And some example:
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.verisign.com/");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} finally {
httpget.releaseConnection();
}
By now, I write this:
HttpClient client = new HttpClient();
HttpMethod get = new GetMethod("https://mms.nw.ru");
//get.setDoAuthentication(true);
try {
int status = client.executeMethod(get);
System.out.println(status);
BufferedInputStream is = new BufferedInputStream(get.getResponseBodyAsStream());
int r=0;byte[] buf = new byte[10];
while((r = is.read(buf)) > 0) {
System.out.write(buf,0,r);
}
} catch(Exception ex) {
ex.printStackTrace();
}
As a result I have a set of errors:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1627)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:204)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:198)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:994)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:142)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:533)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:471)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:904)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1132)
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:643)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:78)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:828)
at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2116)
at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)
at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)
at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
at simpleapachehttp.Main.main(Main.java:41)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:302)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:205)
at sun.security.validator.Validator.validate(Validator.java:235)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:147)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:230)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:270)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:973)
... 17 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:191)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:255)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:297)
... 23 more
What have I to do to create simplest SSL connection?
(Probably without KeyManager and Trust manager etc. while.)
https://mms.nw.ru uses a self-signed certificate that's not in the default trust manager set. To resolve the issue, do one of the following:
Configure SSLContext with a TrustManager that accepts any certificate (see below).
Configure SSLContext with an appropriate trust store that includes your certificate.
Add the certificate for that site to the default Java trust store.
Here's a program that creates a (mostly worthless) SSL Context that accepts any certificate:
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class SSLTest {
public static void main(String [] args) throws Exception {
// configure the SSLContext with a TrustManager
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
SSLContext.setDefault(ctx);
URL url = new URL("https://mms.nw.ru");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
System.out.println(conn.getResponseCode());
conn.disconnect();
}
private static class DefaultTrustManager implements X509TrustManager {
#Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
#Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
}
https://mms.nw.ru likely uses a certificate not issued by a certification authority. Consequently, you need to add the certificate to your trusted Java key store as explained in unable to find valid certification path to requested target:
When working on a client that works
with an SSL enabled server running in
https protocol, you could get error
'unable to find valid certification
path to requested target' if the
server certificate is not issued by
certification authority, but a self
signed or issued by a private CMS.
Don't panic. All you need to do is to
add the server certificate to your
trusted Java key store if your client
is written in Java. You might be
wondering how as if you can not access
the machine where the server is
installed. There is a simple program
can help you. Please download the Java
program and run
% java InstallCert _web_site_hostname_
This program opened a connection to
the specified host and started an SSL
handshake. It printed the exception
stack trace of the error that occured
and shows you the certificates used by
the server. Now it prompts you add the
certificate to your trusted KeyStore.
If you've changed your mind, enter
'q'. If you really want to add the
certificate, enter '1', or other
numbers to add other certificates,
even a CA certificate, but you usually
don't want to do that. Once you have
made your choice, the program will
display the complete certificate and
then added it to a Java KeyStore named
'jssecacerts' in the current
directory.
To use it in your program, either
configure JSSE to use it as its trust
store or copy it into your
$JAVA_HOME/jre/lib/security directory.
If you want all Java applications to
recognize the certificate as trusted
and not just JSSE, you could also
overwrite the cacerts file in that
directory.
After all that, JSSE will be able to
complete a handshake with the host,
which you can verify by running the
program again.
To get more details, you can check out
Leeland's blog No more 'unable to find
valid certification path to requested
target'
In addition to Pascal Thivent's correct answer, another way is to save the certificate from Firefox (View Certificate -> Details -> export) or openssl s_client and import it into the trust store.
You should only do this if you have a way to verify that certificate. Failing that, do it the first time you connect, it will at least give you an error if the certificate changes unexpectedly on subsequent connections.
To import it in a trust store, use:
keytool -importcert -keystore truststore.jks -file servercert.pem
By default, the default trust store should be $JAVA_HOME/jre/lib/security/cacerts and its password should be changeit, see JSSE Reference guide for details.
If you don't want to allow that certificate globally, but only for these connections, it's possible to create an SSLContext for it:
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream("/.../truststore.jks");
ks.load(fis, null);
// or ks.load(fis, "thepassword".toCharArray());
fis.close();
tmf.init(ks);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
Then, you need to set it up for Apache HTTP Client 3.x by implementing one if its SecureProtocolSocketFactory to use this SSLContext. (There are examples here).
Apache HTTP Client 4.x (apart from the earliest version) has direct support for passing an SSLContext.
For Apache HttpClient 4.5+ & Java8:
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial((chain, authType) -> true).build();
SSLConnectionSocketFactory sslConnectionSocketFactory =
new SSLConnectionSocketFactory(sslContext, new String[]
{"SSLv2Hello", "SSLv3", "TLSv1","TLSv1.1", "TLSv1.2" }, null,
NoopHostnameVerifier.INSTANCE);
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(sslConnectionSocketFactory)
.build();
But if your HttpClient use a ConnectionManager for seeking connection, e.g. like this:
PoolingHttpClientConnectionManager connectionManager = new
PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
The HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory) has no effect, the problem is not resolved.
Because that the HttpClient use the specified connectionManager for seeking connection and the specified connectionManager haven't register our customized SSLConnectionSocketFactory. To resolve this, should register the The customized SSLConnectionSocketFactory in the connectionManager. The correct code should like this:
PoolingHttpClientConnectionManager connectionManager = new
PoolingHttpClientConnectionManager(RegistryBuilder.
<ConnectionSocketFactory>create()
.register("http",PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslConnectionSocketFactory).build());
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
The Apache HttpClient 4.5 way:
org.apache.http.ssl.SSLContextBuilder sslContextBuilder = SSLContextBuilder.create();
sslContextBuilder.loadTrustMaterial(new org.apache.http.conn.ssl.TrustSelfSignedStrategy());
SSLContext sslContext = sslContextBuilder.build();
org.apache.http.conn.ssl.SSLConnectionSocketFactory sslSocketFactory =
new SSLConnectionSocketFactory(sslContext, new org.apache.http.conn.ssl.DefaultHostnameVerifier());
HttpClientBuilder httpClientBuilder = HttpClients.custom().setSSLSocketFactory(sslSocketFactory);
httpClient = httpClientBuilder.build();
NOTE: org.apache.http.conn.ssl.SSLContextBuilder is deprecated and org.apache.http.ssl.SSLContextBuilder is the new one (notice conn missing from the latter's package name).
From http://hc.apache.org/httpclient-3.x/sslguide.html:
Protocol.registerProtocol("https",
new Protocol("https", new MySSLSocketFactory(), 443));
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.whatever.com/");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} finally {
httpget.releaseConnection();
}
Where MySSLSocketFactory example can be found here. It references a TrustManager, which you can modify to trust everything (although you must consider this!)
want to paste the answer here:
in Apache HttpClient 4.5.5
How to handle invalid SSL certificate with Apache client 4.5.5?
HttpClient httpClient = HttpClients
.custom()
.setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build())
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
Once you have a Java Cert Store (by using the great InstallCert class created above), you can get java to use it by passing the "javax.net.ssl.trustStore" param at java startup.
Ex:
java -Djavax.net.ssl.trustStore=/path/to/jssecacerts MyClassName
Another issue you may run into with self signed test certs is this:
java.io.IOException: HTTPS hostname wrong: should be ...
This error occurs when you are trying to access a HTTPS url. You might have already installed the server certificate to your JRE's keystore. But this error means that the name of the server certificate does not match with the actual domain name of the server that is mentioned in the URL. This normally happens when you are using a non CA issued certificate.
This example shows how to write a HttpsURLConnection DefaultHostnameVerifier that ignore the certificates server name:
http://www.java-samples.com/showtutorial.php?tutorialid=211
EasySSLProtocolSocketFactory was giving me problems so I ended up implementing my own ProtocolSocketFactory.
First you need to register it:
Protocol.registerProtocol("https", new Protocol("https", new TrustAllSSLSocketFactory(), 443));
HttpClient client = new HttpClient();
...
Then implement ProtocolSocketFactory:
class TrustAllSSLSocketFactory implements ProtocolSocketFactory {
public static final TrustManager[] TRUST_ALL_CERTS = new TrustManager[]{
new X509TrustManager() {
public void checkClientTrusted(final X509Certificate[] certs, final String authType) {
}
public void checkServerTrusted(final X509Certificate[] certs, final String authType) {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
private TrustManager[] getTrustManager() {
return TRUST_ALL_CERTS;
}
public Socket createSocket(final String host, final int port, final InetAddress clientHost,
final int clientPort) throws IOException {
return getSocketFactory().createSocket(host, port, clientHost, clientPort);
}
#Override
public Socket createSocket(final String host, final int port, final InetAddress localAddress,
final int localPort, final HttpConnectionParams params) throws IOException {
return createSocket(host, port);
}
public Socket createSocket(final String host, final int port) throws IOException {
return getSocketFactory().createSocket(host, port);
}
private SocketFactory getSocketFactory() throws UnknownHostException {
TrustManager[] trustAllCerts = getTrustManager();
try {
SSLContext context = SSLContext.getInstance("SSL");
context.init(null, trustAllCerts, new SecureRandom());
final SSLSocketFactory socketFactory = context.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
return socketFactory;
} catch (NoSuchAlgorithmException | KeyManagementException exception) {
throw new UnknownHostException(exception.getMessage());
}
}
}
Note: This is with HttpClient 3.1 and Java 8
For a way to easily add hosts you trust at runtime without throwing out all checks, try the code here: http://code.google.com/p/self-signed-cert-trust-manager/.
I happened to face the same issue, all of a sudden all my imports were missing. I tried deleting all the contents in my .m2 folder. And trying to re-import everything , but still nothing worked.
Finally what I did was opened the website for which the IDE was complaining that it couldn't download in my browser. And saw the certificate it was using, and saw in my
$ keytool -v -list PATH_TO_JAVA_KEYSTORE
Path to my keystore was /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
that particular certificate was not there.
So all you have to do is put the certificate into the JAVA JVM keystore again.
It can be done using the below command.
$ keytool -import -alias ANY_NAME_YOU_WANT_TO_GIVE -file PATH_TO_YOUR_CERTIFICATE -keystore PATH_OF_JAVA_KEYSTORE
If it asks for password, try the default password 'changeit'
If you get permission error when running the above command.
In windows open it in administration mode.
In mac and unix use sudo.
After you have successfully added the key,
You can view it using :
$ keytool -v -list /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
You can view just the SHA-1 using teh command
$ keytool -list /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
This link explains the requirement you have step by step. If You are not really concerned which certificate you can proceed with the process in below link.
Note You might want to double check what you are doing since, this is a unsafe operation.
Using the InstallCert to generate the jssecacerts file and do
-Djavax.net.ssl.trustStore=/path/to/jssecacerts worked great.
I'm useing httpclient 3.1.X ,and this works for me
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager trustManager = new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[]{trustManager}, null);
SslContextSecureProtocolSocketFactory socketFactory = new SslContextSecureProtocolSocketFactory(sslContext,false);
Protocol.registerProtocol("https", new Protocol("https", (ProtocolSocketFactory) socketFactory, 443));//同样会影响到HttpUtils
} catch (Throwable e) {
e.printStackTrace();
}
public class SslContextSecureProtocolSocketFactory implements SecureProtocolSocketFactory {
private SSLContext sslContext;
private boolean verifyHostname;
public SslContextSecureProtocolSocketFactory(SSLContext sslContext, boolean verifyHostname) {
this.verifyHostname = true;
this.sslContext = sslContext;
this.verifyHostname = verifyHostname;
}
public SslContextSecureProtocolSocketFactory(SSLContext sslContext) {
this(sslContext, true);
}
public SslContextSecureProtocolSocketFactory(boolean verifyHostname) {
this((SSLContext)null, verifyHostname);
}
public SslContextSecureProtocolSocketFactory() {
this((SSLContext)null, true);
}
public synchronized void setHostnameVerification(boolean verifyHostname) {
this.verifyHostname = verifyHostname;
}
public synchronized boolean getHostnameVerification() {
return this.verifyHostname;
}
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException {
SSLSocketFactory sf = this.getSslSocketFactory();
SSLSocket sslSocket = (SSLSocket)sf.createSocket(host, port, clientHost, clientPort);
this.verifyHostname(sslSocket);
return sslSocket;
}
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
if(params == null) {
throw new IllegalArgumentException("Parameters may not be null");
} else {
int timeout = params.getConnectionTimeout();
Socket socket = null;
SSLSocketFactory socketfactory = this.getSslSocketFactory();
if(timeout == 0) {
socket = socketfactory.createSocket(host, port, localAddress, localPort);
} else {
socket = socketfactory.createSocket();
InetSocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
InetSocketAddress remoteaddr = new InetSocketAddress(host, port);
socket.bind(localaddr);
socket.connect(remoteaddr, timeout);
}
this.verifyHostname((SSLSocket)socket);
return socket;
}
}
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
SSLSocketFactory sf = this.getSslSocketFactory();
SSLSocket sslSocket = (SSLSocket)sf.createSocket(host, port);
this.verifyHostname(sslSocket);
return sslSocket;
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
SSLSocketFactory sf = this.getSslSocketFactory();
SSLSocket sslSocket = (SSLSocket)sf.createSocket(socket, host, port, autoClose);
this.verifyHostname(sslSocket);
return sslSocket;
}
private void verifyHostname(SSLSocket socket) throws SSLPeerUnverifiedException, UnknownHostException {
synchronized(this) {
if(!this.verifyHostname) {
return;
}
}
SSLSession session = socket.getSession();
String hostname = session.getPeerHost();
try {
InetAddress.getByName(hostname);
} catch (UnknownHostException var10) {
throw new UnknownHostException("Could not resolve SSL sessions server hostname: " + hostname);
}
X509Certificate[] certs = (X509Certificate[])((X509Certificate[])session.getPeerCertificates());
if(certs != null && certs.length != 0) {
X500Principal subjectDN = certs[0].getSubjectX500Principal();
List cns = this.getCNs(subjectDN);
boolean foundHostName = false;
Iterator i$ = cns.iterator();
AntPathMatcher matcher = new AntPathMatcher();
while(i$.hasNext()) {
String cn = (String)i$.next();
if(matcher.match(cn.toLowerCase(),hostname.toLowerCase())) {
foundHostName = true;
break;
}
}
if(!foundHostName) {
throw new SSLPeerUnverifiedException("HTTPS hostname invalid: expected \'" + hostname + "\', received \'" + cns + "\'");
}
} else {
throw new SSLPeerUnverifiedException("No server certificates found!");
}
}
private List<String> getCNs(X500Principal subjectDN) {
ArrayList cns = new ArrayList();
StringTokenizer st = new StringTokenizer(subjectDN.getName(), ",");
while(st.hasMoreTokens()) {
String cnField = st.nextToken();
if(cnField.startsWith("CN=")) {
cns.add(cnField.substring(3));
}
}
return cns;
}
protected SSLSocketFactory getSslSocketFactory() {
SSLSocketFactory sslSocketFactory = null;
synchronized(this) {
if(this.sslContext != null) {
sslSocketFactory = this.sslContext.getSocketFactory();
}
}
if(sslSocketFactory == null) {
sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
}
return sslSocketFactory;
}
public synchronized void setSSLContext(SSLContext sslContext) {
this.sslContext = sslContext;
}
}
For HttpClient, we can do this :
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
SSLContext.setDefault(ctx);
String uri = new StringBuilder("url").toString();
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
HttpClient client = HttpClientBuilder.create().setSSLContext(ctx)
.setSSLHostnameVerifier(hostnameVerifier).build()
follow the instruction given below for Java 1.7, to create an SSL certificate using InstallCert.java program file.
https://github.com/escline/InstallCert
you must restart the tomcat
Used the following along with DefaultTrustManager and it worked in httpclient like charm. Thanks a ton!! #Kevin and every other contributor
SSLContext ctx = null;
SSLConnectionSocketFactory sslsf = null;
try {
ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
SSLContext.setDefault(ctx);
sslsf = new SSLConnectionSocketFactory(
ctx,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
} catch (Exception e) {
e.printStackTrace();
}
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();

Categories