PoolingHttpClientConnectionManager: How to do Https requests? - java

I'm currently trying to do multiple HttpGet requests at the same time with CloseableHttpClient.
I googled on how to do that and the answer was to use a PoolingHttpClientConnectionManager.
At this point I got this:
PoolingHttpClientConnectionManager cManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cManager)
.build();
Then I tried a HttpGet request to http://www.google.com and everything worked fine.
Then I created a truststore via cmd and imported the certificate of the targeted website, setup a SSLConnectionSocketFactory with my truststore and set the SSLSocketFactory of httpClient:
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream inputStream = new FileInputStream(new File("myTrustStore.truststore"));
trustStore.load(inputStream, "nopassword".toCharArray());
inputStream.close();
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStore).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
PoolingHttpClientConnectionManager cManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.setConnectionManager(cManager)
.build();
If I try to execute a Https HttpGet then I get a PKIX path building failed exception.
If I do the same without .setConnectionManager(cManager) everything works fine.
Can anyone of you tell me how I can get this to work? (Don't worry, I don't create any ddos tool)
Thanks in advance!
P.S.: I'm using HttpComponents 4.3.1

Found the answer:
https://stackoverflow.com/a/19950935/1223253
Just had to add
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create().register("https", sslsf).build();
and pass socketFactoryRegistry as parameter to the constructor of PoolingHttpClientConnectionManager.
Now it works just fine :)

Related

How to use SSL Client certificate with Apache commons http client

My application is using Apache Commons HTTP Client to consume HTTP service URL. Now we have to move over HTTPS endpoint URL. To consume the same, we received SSL Client Certificate. How we can use .JKS with password while consuming HTTPS URL ? (Due to application limitations cant use other APIs)
KeyStore identityKeyStore = KeyStore.getInstance("JKS");
FileInputStream identityKeyStoreFile = new FileInputStream(new File(certificatePath));
identityKeyStore.load(identityKeyStoreFile, password.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(identityKeyStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(identityKeyStore, password.toCharArray());
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(), null);
SSLContext.setDefault(sslContext);
PostMethod post = new PostMethod("https://url");
HttpClient httpClient = new HttpClient();
String reqMessage = getSolaceRequestMessage(message,hostName,port,authentication);
Part[] parts = {
new StringPart("reqMessage", message),
};
post.setRequestEntity(
new MultipartRequestEntity(parts, post.getParams())
);
httpClient.executeMethod(post);
The *.jks we use in the back service part.
I can give you a example of my project Java Spring boot, I change http --> https in my back service and I added my certificate in Nginx.
Example of https simple services
When you changed back service you can call https directly in your front application(ex.web angular).
I used below implementation which worked for me as had limitation not to upgrade the http client libraries.
System.setProperty(JAVAX_NET_SSL_TRUSTSTORE, "H://certificateFile.jks");
System.setProperty(JAVAX_NET_SSL_TRUSTSTORE_KEY, "abcd");

Ignore SSL Warnings with Apache HttpClient 4.5 and Spring RestTemplate

We have an internal service that we would like to start making calls to over HTTPS. The service is only available internally, so I'm not worried about accepting all self-signed warnings. This is what I'm using:
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(REST_USER, REST_PASS));
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 client = HttpClients.custom()
.setDefaultCredentialsProvider(credentialsProvider)
.setSSLSocketFactory(sslsf)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(client);
RestTemplate restTemplate = new RestTemplate(requestFactory);
But I'm getting the following error:
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost/addrgeo/standardUSAddresses": sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is 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
I even tried creating a PoolingHttpClientConnectionManager:
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(REST_USER, REST_PASS));
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(credentialsProvider)
.setSSLSocketFactory(sslsf)
.setConnectionManager(cm)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
But I get the same error.
We are wrapping our calls with Hystrix, but I don't know why that would have any impact on the actual call itself.
I took a look at this question:
Why can't I find the truststore for an SSL handshake?
But none of the answers seem to get me where I need to be.
What am I doing wrong?
Solution
Well, I feel like a fool...
Turns out it was a weird caching issue with IntelliJ. Maven was still using the old source code. The solution was to close IntelliJ, run a mvn clean install, opening and invalidating caches in IntelliJ, and re-importing the project worked. For good measure, I also refreshed the file status under VCS and performed a Synchronize.

HttpClient 4.5.2 Proxy setting

I need to call third party web service via proxy.
I only have information below:
- Third party https IP & Port
- Proxy http IP & Port
May I know I need credential : username & Password for the Proxy?
Part of my code below:-
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
DefaultProxyRoutePlanner routePlanner = new
DefaultProxyRoutePlanner(proxy);
CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
RequestConfig config =RequestConfig.custom().setProxy(proxy).build();
HttpPost httppost = new HttpPost(queryUrl);
httppost.setConfig(config);
CloseableHttpResponse httpResponse = httpclient.execute(httppost);
Unfortunately, Proxy server reponse me "Access denied. Authentication is required."
Any idea for this problem?

Jetty http2 client to ignore self-signed cert

I have an http2 server at https://ec2-52-57-54-142.eu-central-1.compute.amazonaws.com/ with a self-signed cert. And I have a jetty http2 client that simply posts things to it. For some reason, I'm not getting ignoring self-signed cert to work. Here's a snippet of relevant code
SslContextFactory factory = new SslContextFactory(true);
factory.setTrustAll(true);
factory.setValidateCerts(false);
factory.setValidatePeerCerts(false);
factory.setEndpointIdentificationAlgorithm(null);
SSLContext sslContext = factory.getSslContext();
if(null == sslContext) {
sslContext = SSLContext.getInstance("TLS");
}
TrustManager[] verifiers = new TrustManager[] {...// some dummy trust manager that always passes};
sslContext.init(null, verifiers, null);
factory.setSslContext(sslContext);
HttpClientTransportOverHTTP2 httpClientTransportOverHTTP2
= new HttpClientTransportOverHTTP2(new HTTP2Client());
HttpClient httpClient = new HttpClient(httpClientTransportOverHTTP2, factory);
Request request = httpClient.POST(destination);
ContentProvider contentProvider = new InputStreamContentProvider(new StringInputStream(payload));
request.content(contentProvider);
ContentResponse response = request.send();
And I get these stacktrace
Caused by: java.util.concurrent.ExecutionException: java.nio.channels.ClosedChannelException
at org.eclipse.jetty.client.util.FutureResponseListener.getResult(FutureResponseListener.java:118)
at org.eclipse.jetty.client.util.FutureResponseListener.get(FutureResponseListener.java:101)
at org.eclipse.jetty.client.HttpRequest.send(HttpRequest.java:652)
at my code
... 34 more
Caused by: java.nio.channels.ClosedChannelException
at org.eclipse.jetty.io.WriteFlusher.onClose(WriteFlusher.java:498)
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.onIncompleteFlush(SslConnection.java:409)
at org.eclipse.jetty.io.AbstractEndPoint$2.onIncompleteFlush(AbstractEndPoint.java:54)
at org.eclipse.jetty.io.WriteFlusher.write(WriteFlusher.java:322)
at org.eclipse.jetty.io.AbstractEndPoint.write(AbstractEndPoint.java:140)
at org.eclipse.jetty.http2.HTTP2Flusher.process(HTTP2Flusher.java:243)
at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:241)
at org.eclipse.jetty.util.IteratingCallback.succeeded(IteratingCallback.java:365)
at org.eclipse.jetty.http2.HTTP2Flusher.succeeded(HTTP2Flusher.java:258)
at org.eclipse.jetty.io.WriteFlusher$PendingState.complete(WriteFlusher.java:269)
at org.eclipse.jetty.io.WriteFlusher.completeWrite(WriteFlusher.java:394)
at org.eclipse.jetty.io.ssl.SslConnection$1.run(SslConnection.java:101)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
... 1 more
When I step through things with a debugger, I see something about NOT_HANDSHAKE in one of the connection objects.
I really don't care about verifying anything. I just want to connect over HTTP2/TLS. I searched for many different terms, but they all end up with more or less the same thing (setTrustAll, custom TrustManager, etc)
Any help? Thanks!
P.S. Jetty version 9.3.12
Your client code is correct, although redundant.
It is enough to do:
SslContextFactory sslContextFactory = new SslContextFactory(true);
HTTP2Client http2Client = new HTTP2Client();
HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP2(http2Client), sslContextFactory);
httpClient.start();
ContentResponse response = httpClient.GET("https://ec2-52-57-54-142.eu-central-1.compute.amazonaws.com/");
If you enable DEBUG logging on the Jetty HTTP/2 client, you will see that the client receives:
2016-10-05 09:20:33.102:DBUG:oejhp.Parser:qtp1897115967-15: Parsed GO_AWAY frame header from java.nio.HeapByteBuffer[pos=9 lim=35 cap=16384]
2016-10-05 09:20:33.103:DBUG:oejh.HTTP2Session:qtp1897115967-15: Received GoAwayFrame#3bc447d3,0/INADEQUATE_SECURITY_ERROR/Unknown error code
So the problem is that the server thinks that the security is inadequate (the GOAWAY frame arrives with error code INADEQUATE_SECURITY_ERROR).
At this point, the problem is on the server. You have to figure out why the server thinks the security is inadequate. Probably just a matter of configuration on the server.
Try
transport.setUseALPN(false);

Any way to disable SSLv2 backwards compatibility mode for Apache HttpClient?

I am trying to connect to an HTTPs URL with Apache HttpClient 4.1....
HttpGet httpget = new HttpGet("https://federation/galaxy-class/enterprise/getSheildFrequencies");
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpResponse response = httpclient.execute(httpget);
During the connection process, I get the below exception...
Caught: javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
When I turn on debugging, I see the below..
main, WRITE: TLSv1 Handshake, length = 81
main, WRITE: SSLv2 client hello message, length = 110
main, handling exception: java.net.SocketException: Connection reset
So it seems like my client did the handshake in TLSv1 but then sent a client hello in SSLv2 which the server didn't like (it dropped the connection because it doesn't support SSLv2 backwards compatibility mode).
Is there any way to tell Apache HttpClient not to do that? Or is this something configured at the underlying JRE (I am using 1.6)?
UPDATE
As bmargulies suggested, I tried to make my own socket factory and configure it to only allow the protocols I want....
def supportedProtocols = new String[2]
supportedProtocols[0] = 'SSLv3'
supportedProtocols[1] = 'TLSv1'
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
SSLContext.getDefault(),supportedProtocols,
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", socketFactory)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
CloseableHttpClient client = HttpClients.custom().setConnectionManager(cm).build();
HttpResponse response = client.execute(httpget);
But this gives another exception...
javax.net.ssl.SSLException: java.lang.RuntimeException: Could not generate DH keypair
Use the current version of the http components
Use the HttpClientBuilder
Make your own socket factory and configure it to only allow the protocols you want.
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainSocketFactory.INSTANCE)
.register("https", new SSLSocketFactory(sslcontext, hostnameVerifier))
.build();
Remove SSLv2ClientHello from the SSLContext's enabled protocols.

Categories