Disabling SSL checking for Spring web-client - java

The following code is what I am using to try and build a web client instance that can talk to a https server with an invalid certificate.
SslContext sslContext = SslContextBuilder
.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
HttpClient httpClient = HttpClient
.create()
.secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
WebClient client = WebClient
.builder()
.clientConnector(connector)
// ...
.build();
The purpose of this is to make the web client not check the ssl however when ran the JVM crashes with an error "javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure". Could someone please point me into the right direction as previous SO posts dont seem to fix the problem and lead to the same handshake error.

Just in case anyone gets this error with their spring webclient, the solution that ended up working for me was adding protocols and ciphers into the SSLcontext.
Iterable<String> allowedCiphers = List.of("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384");
SslContext sslContext = SslContextBuilder
.forClient()
.protocols("SSLv3","TLSv1","TLSv1.1","TLSv1.2")
.ciphers(allowedCiphers)
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();

Related

How to make https requests using a HAPI FHIR client

Is there any example of how make a HTTPS call with a hapi fhir client ?
FhirContext ctx = new FhirContext();
IGenericClient client = ctx.newRestfulGenericClient("https://fhirtest.uhn.ca/base");
By default the above code will not work as the server will require SSL authentication.
how do I add SSL authentication to the hapi client ??
The next example shows how to connect to a FHIR server using https while using the HAPI FHIR client. Please be aware that this example accepts all certificates. To make it secure you should specify a truststore and a different hostname verifier.
FhirContext ctx = new FhirContext();
KeyStore truststore = null;
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(truststore, new TrustSelfSignedStrategy()).build();
HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslFactory).build();
ctx.getRestfulClientFactory().setHttpClient(httpClient);
IGenericClient client = ctx.newRestfulGenericClient("https://fhirtest.uhn.ca/base");

Java HttpClient with TLS/SSLContext through proxy

I want to make a HTTP call to server that uses TLS to authenticate. Moreover server needs my IP to be whitelisted, so with AWS Lambda I need to use proxy. What I want to achieve is HTTP POST request with TLS that goes through proxy.
To achieve TLS protocol I use KeyStore with loaded certs and private key.
Making a call without proxy (locally from whitelisted IP) works, so I assume keyStore is configured correctly.
Here is how I build httpClient (it's java.net.http.HttpClient):
var keyManagerFactory = KeyManagerFactory.getInstance("PKIX");
keyManagerFactory.init(keyStore, null);
var trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
trustManagerFactory.init(keyStore, null);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
URI proxyUri = config.getProxyUri(); // this is injected object with preloaded config parameters
HttpClient httpClient = HttpClient.newBuilder()
.sslContext(sslContext)
.proxy(
ProxySelector.of(
InetSocketAddress.createUnresolved(proxyUri.getHost(), proxyUri.getPort())))
.build();
Now making a request:
String body = createRequestBody(); // creates string with JSON
HttpRequest request = HttpRequest.newBuilder()
.uri(config.getServiceUri()) // same config as in example above
.header("Content-Type", "application/json")
.POST(BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = httpClient.send(request, BodyHandlers.ofString());
Calling .send(...) causes
java.io.IOException: Tunnel failed, got: 403
# java.net.http/jdk.internal.net.http.HttpClientImpl.send(Unknown Source)
# java.net.http/jdk.internal.net.http.HttpClientFacade.send(Unknown Source)
# (method we are in above example)
Proxy doesn't need any authentication and in other AWS Lambda I've seen this proxy working with builder using only .proxy(...) method just like in the example above. So the only thing that is different is this .sslContext(...).
Do I need some more sslContext configuration? I've been searching for some examples with TLS through proxy, but I've not managed to find anything.
HttpClient.Builder Docs doesn't say anything about proxy with sslContext either.
Thanks for help!
As daniel wrote in a comment
It would seem that you have insufficient permission to access the service you're trying to use
It turned out to be proxy config that was blocking traffic to that specific host and port.
There is nothing wrong with the code above. After a change in proxy settings to it works as expected.
Thanks for help!

Ignore self-signed certificates in Apache HTTPClient 4.5

I am trying to accept all certificates, and/or accept self-signed certificates using Apache HTTPClient version 4.5 (tutorial link here)
I've been going through solutions to this problem from a bunch of posts on SO. So far none of them have worked.
I keep getting this error: Error while trying to execute request. javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
Apache Docs:
Apache v4.5 tutorial
SSL/TLS customization
Apache has a guide for version 3, but not version 4.
Related StackOverflow Questions - Here's some links of the solutions I've tried:
Ignoring SSL certificate in Apache HttpClient 4.3
How to ignore SSL certificate errors in Apache HttpClient 4.0
Ignore SSL Certificate Errors with Java
Need to trust all the certificates during the development using Spring
How to handle invalid SSL certificates with Apache HttpClient?
Note that in all these examples I am also passing a cookie store and a proxy credentials provider that I defined earlier. These are working, I'm just trying to add SSL support.
Try #1
Create my own ssl context with SSLContextBuilder and trust all self signed strategies with TrustSelfSignedStrategy.
SSLContextBuilder sshbuilder = new SSLContextBuilder();
sshbuilder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sshbuilder.build());
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.setDefaultCookieStore(cookieStore)
.setSSLSocketFactory(sslsf)
.build();
RESULT: Didn't work. Got Error while trying to execute request. javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
Try #2
Same as above, but add a PoolingHttpClientConnectionManager
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(),SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new PlainConnectionSocketFactory())
.register("https", sslsf)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
cm.setMaxTotal(2000);//max connection
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.setDefaultCookieStore(cookieStore)
.setSSLSocketFactory(sslsf)
.setConnectionManager(cm)
.build();
RESULT: Didn't work. Got Error while trying to execute request. javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
Try #3
Simply accept ALL certificates by overriding the TrustStrategy (this is not recommended)
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustStrategy() {
#Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
});
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(),
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.setDefaultCookieStore(cookieStore)
.setSSLSocketFactory(sslsf)
.build();
RESULT: Didn't work. Got Error while trying to execute request. javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
Try #4
I found something useful from this answer:
As of version 4.5 HttpClient disables SSLv3 protocol version by
default
Here's the solution he gave:
SSLContext sslcontext = SSLContexts.createSystemDefault();
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
sslcontext, new String[] { "TLSv1", "SSLv3" }, null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", sslConnectionSocketFactory)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.setDefaultCookieStore(cookieStore)
.setSSLSocketFactory(sslConnectionSocketFactory)
.setConnectionManager(cm)
.build();
RESULT: Didn't work. Got Error while trying to execute request. javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
I'm using Apache HttpClient 4.5.3 and none of the above solutions helped. I always got the error
PKIX Path building failed
.
I found the solution in http://www.baeldung.com/httpclient-ssl
Here's my code:
try {
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(null, (certificate, authType) -> true).build();
httpClient = HttpClients.custom().setSSLContext(sslContext)
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.build();
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
e.printStackTrace();
}
Very often you not only need to support self signed certificates, but also invoke multi-threaded requests, and so use a pooling connection manager. Here's how I do it:
private CloseableHttpClient newClient() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
SSLContext context = SSLContexts.custom()
.loadTrustMaterial(TrustSelfSignedStrategy.INSTANCE)
.build();
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", new SSLConnectionSocketFactory(context, NoopHostnameVerifier.INSTANCE))
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
return HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
}
One of the above approaches should work in case of self-signed certificates, but the weird thing is you are getting same exception in all the approaches.
I feel during SSL session establishment or handshaking protocol is not being accepted either by client or by server.
The best solution here is to debug the application.
In case of tomcat, add -Djavax.net.debug=all in setenv.sh or setenv.bat files and then restart the server.
Or you can follow this tutorial.
The OP just needed to change the port when connecting to SSL:
//For HTTPS
HttpHost httpstarget = new HttpHost("mysite.com", 443, "https");
//For HTTP
HttpHost httptarget = new HttpHost("mysite.com", 80, "http");
This problem is about SSL connection. When you try to connect to some resource https protocol requires to create secured connection. That means only your browser and website server know what data is being sent in requests bodies. This security is achieved by ssl certificates that stored on website and are being downloaded by your browser (or any other client, Apache Http Client in our case) with first connection to host. There are RSA256 encryption and many other cool things around.
But in the end of a day: In case certificate is not registered or is invalid you will see certificate error (HTTPS connection is not secure).
To fix certificate error website provider need to buy it for particular website or fix somehow e.g. https://www.register.com/ssl-certificates
however there is an bypass when you skip ssl verification with
(s, sslSession) -> true
that is security violation because you are not 100% sure that your data is secured, however this solution can used for testing or configuration when use test data and trusted websites
public static HttpClient newClient() {
SSLContext sslcontext = null;
try {
sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
e.printStackTrace();
}
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslcontext,
(s, sslSession) -> true);
return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory)
.build();
}

jersey-client 2.22.2 - How to set SunPKCS11 keystore on SslConfigurator properly?

I have been attempting to have my jersey client do a ssl client authentication with my Jersey/Grizzly Rest api. Other clients are successful handshaking with this server, but I am having trouble with my java client using Jersey client. When I run the code below, the keystore is successfully loaded and when the SslConfigurator's createSSLContext() is called, the ssl debug output shows this keystore properly being accessed and my private keys found.
However, when the Client's WebTarget is used, the ssl debug output shows the handshake is happening with the default keystore JKS. Why isn't the ClientBuilder using this keystore from the SSLContext?
File tmpConfigFile = File.createTempFile("pkcs11-", "conf");
tmpConfigFile.deleteOnExit();
PrintWriter configWriter = new PrintWriter(new FileOutputStream(tmpConfigFile), true);
configWriter.println("name=ActiveClient");
configWriter.println("library=\"C:\\\\Program Files\\\\ActivIdentity\\\\ActivClient\\\\acpkcs211.dll\"");
configWriter.println("slotListIndex=0");
SunPKCS11 provider = new SunPKCS11(tmpConfigFile.getAbsolutePath());
Security.addProvider(provider);
// KeyStore keyStore = KeyStore.getInstance("PKCS11", provider);
KeyStore keyStore = KeyStore.getInstance("PKCS11");
keyStore.load(null, null);
ClientConfig config = new ClientConfig();
SslConfigurator sslConfig = SslConfigurator.newInstance()
.keyStore(keyStore)
.keyStorePassword("mypin")
.keyStoreType("PKCS11")
.trustStoreFile(TRUSTORE_CLIENT_FILE)
.trustStorePassword(TRUSTSTORE_CLIENT_PWD)
.securityProtocol("TLS");
final SSLContext sslContext = sslConfig.createSSLContext();
Client client = ClientBuilder
.newBuilder().hostnameVerifier(new MyHostnNameVerifier())
.sslContext(sslContext)
.build();
WebTarget target = client.target("https://localhost:8443/appname/resources/employees?qparam=something");
Response res = target.request().accept(MediaType.APPLICATION_JSON).get();
This code actually worked. The problem was that my server's trust certificate wasn't available for the smart card cert that it needed to trust. I added the correct certs to the truststore on the server and then it worked. The ssl debug messages weren't very clear.
I've run into many issues this time and I found a way to achieve my goals. In your example I can not see any use of ClientConfig config instance. This worked for me:
ClientConfig config = new ClientConfig();
config.connectorProvider(new ApacheConnectorProvider());
Client client = ClientBuilder.newBuilder().hostnameVerifier(new MyHostnNameVerifier())
.sslContext(sslContext).withConfig(config).build();
I found ApacheConnectorProvider more suitable for connections using secure layers or proxies (witch was another huge problem I solved).

Jira REST https-requests via Java

I want to connect to a https jira server using the jersey client (version 1.1.9).
How do I need to configure the security options to make use of the REST-API?
I followed these instructions:
Accessing secure restful web services using jersey client
But the first link in the answer is broken and I don't know how to configure the truststore and the keystore. Where do I get these files?
I switched to jersey-client-2.19 and configured the keystore and truststore with the keytool.
System.setProperty("jsse.enableSNIExtension", "false");
SslConfigurator sslConfig = SslConfigurator.newInstance()
.trustStoreFile("C:/Program Files/Java/jre1.8.0_45/lib/security/cacerts.jks")
.trustStorePassword("somepass")
.keyStoreFile("C:/Program Files/Java/jre1.8.0_45/lib/security/keystore.jks")
.keyPassword("somepass");
SSLContext sslContext = sslConfig.createSSLContext();
Client client = ClientBuilder.newBuilder().sslContext(sslContext)
.build();
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic(
JIRA_ADMIN_USERNAME, JIRA_ADMIN_PASSWORD);
client.register(feature);
WebTarget webTarget = client.target(JIRA_URL);
WebTarget projectWebTarget = webTarget.path("project");
Invocation.Builder invocationBuilder = projectWebTarget
.request(MediaType.APPLICATION_JSON);
Response response = invocationBuilder.get();
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
Maybe there is a better way to set the properties for the keystore and truststore. So please let me know.

Categories

Resources