HttpClient 4.3.x, fixing deprecated code to use current HttpClient implementations - java

I had the following code, which still compiles, but they're all deprecated:
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(context, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager clientConnectionManager = base.getConnectionManager();
SchemeRegistry schemeRegistry = clientConnectionManager.getSchemeRegistry();
schemeRegistry.register(new Scheme("https", 443, sslSocketFactory));
return new DefaultHttpClient(clientConnectionManager, base.getParams());
I tried my best to replace it with this portion of the code:
HttpClientBuilder builder = HttpClientBuilder.create();
SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(context, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
builder.setConnectionManager(new BasicHttpClientConnectionManager());
builder.setSSLSocketFactory(sslConnectionFactory);
return builder.build();
As you can see, there are few lines of code from the top post that I don't know how to include on the new portion. How can I add needed code, such as, an alternate SchemeRegistry?

I can not comment yet, but here is a small upgrade to herau's answer since it's deprecated since 4.4, maybe someone will find it useful.
SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(context, NoopHostnameVerifier.INSTANCE);

HttpClientBuilder builder = HttpClientBuilder.create();
SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(context, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
builder.setSSLSocketFactory(sslConnectionFactory);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslConnectionFactory)
.build();
HttpClientConnectionManager ccm = new BasicHttpClientConnectionManager(registry);
builder.setConnectionManager(ccm);
return builder.build();

As manual said, I have replaced library to NoopHostnameVerifier and use it like that:
private static CloseableHttpClient client =
HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();

Related

create HttpClient using useSystemProperties()

I am trying to create one http client using useSystemProperties() as i need to default the ssl properties to that of WAS [like to get the WAS ciphers in runtime ]. And I have to set some max connections and connection manager also to the httpclient. This is a very high traffic rest call.
I have tried 3 ways,
-- This did not set the WAS ssl properties and thus the connection got failed.
httpclient = HttpClients.custom().useSystemProperties()
.setConnectionManager("some value")
.setMaxConnPerRoute("some value")
.setMaxConnTotal("some value")
.setUserAgent("Custom Browser")
.disableCookieManagement().build();
-- This did not set the WAS ssl properties and thus the connection failed.
httpclient1 = HttpClientBuilder.create().useSystemProperties()
.setConnectionManager(connManager)
.setMaxConnPerRoute(maxConnPerRoute)
.setMaxConnTotal(maxConnTotal)
.setUserAgent("Custom Browser")
.disableCookieManagement().build();
-- This one defaulted to WAS ssl configurations and connection was fine but other params are missing here.
httpclient2 = HttpClientBuilder.create().useSystemProperties().build();
Can I really achieve both these options?
You would need to override the SSLConnectionSocketFactory for your ConnectionManager, for example, on the example below will be created default SSLConnectionSocketFactory if you would use useSystemProperties
DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(PublicSuffixMatcherLoader.getDefault());
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
(SSLSocketFactory) SSLSocketFactory.getDefault(), null, null, hostnameVerifier
);
final PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslConnectionSocketFactory)
.build()
);
connManager.setDefaultMaxPerRoute(20);
connManager.setMaxTotal(20);
final HttpClientBuilder builder = HttpClientBuilder
.create()
.setConnectionManager(connManager);
You can also set useSystemProperties() as below to your HttpClient:
HttpClient httpClient = HttpClientBuilder.create()
.setConnectionManager(connectionManager)
.useSystemProperties()
.setDefaultRequestConfig(requestConfig).build();

javax.net.ssl.SSLPeerUnverifiedException: Host name does not match the certificate subject provided by the peer

I follow many links on stackoverflow and tried many solutions, but none of them worked for me. I'm using WSO2 API manager version 1.9.1. I am facing following error:
Exception in thread "main" javax.net.ssl.SSLPeerUnverifiedException: Host name 'XXXXXXXXX' does not match the certificate subject provided by the peer (CN=localhost, O=WSO2, L=Mountain View, ST=CA, C=US)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.verifyHostname(SSLConnectionSocketFactory.java:465)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:395)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:353)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:134)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107)
at com.java.pushNotifications.WSO2DemoClient.main(WSO2DemoClient.java:49)
I developed the following Java code. Please help me what's going wrong here. I need to connect insecure way and allow connections to SSL sites without certs.
public static void main(String[] args) throws ClientProtocolException, IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());
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().setSSLSocketFactory(sslsf)
.setConnectionManager(cm).build();
HttpGet httpGet = new HttpGet("https://XXXXXXXXXX:8243/token");
CloseableHttpResponse response = httpclient.execute(httpGet);
String json =" {\"data\":\"grant_type=password&username=test&password=test123\"}";
try {
HttpPost httpost = new HttpPost(url);
httpost.setHeader("Content-Type", "application/x-www-form-urlencoded");
httpost.setHeader("Authorization", "Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
httpost.setEntity(new StringEntity(json));
HttpResponse httpResponse = httpclient.execute(httpost);
System.out.println(httpResponse.getStatusLine());
}
finally {
response.close();
}
String responseString1 = new BasicResponseHandler().handleResponse(response);
System.out.println("Response : "+responseString1);
}
I have spent an hour trying to fix the same issue. This is what I come up with:
final SSLConnectionSocketFactory sslsf;
try {
sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault(),
NoopHostnameVerifier.INSTANCE);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new PlainConnectionSocketFactory())
.register("https", sslsf)
.build();
final PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
cm.setMaxTotal(100);
httpClient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.setConnectionManager(cm)
.build();
Hopefully, it works and does not use any deprecated code (httpclient 4.4.1).
Replace this
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf)
.setConnectionManager(cm).build();
with
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.setConnectionManager(cm)
.setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
.build();
If the certificate isn't signed (not even self-signed), then you can do
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class TrustAllStrategy implements TrustStrategy {
#Override
public boolean isTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
return true;
}
}
Then
builder.loadTrustMaterial(new TrustAllStrategy());
EDIT: this
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext, //for you this is builder.build()
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
);
Thanks to all the solutions. I have been trying all the solutions available online for 1.5 days now and finally it worked now. Here is the working code
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new PlainConnectionSocketFactory())
.register("https", sslConnectionSocketFactory)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
cm.setMaxTotal(100);
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslConnectionSocketFactory)
.setConnectionManager(cm)
.build();
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(postEntity);
httpPost.expectContinue();
CloseableHttpResponse response = httpclient.execute(httpPost);
This is what I came up with:
SSLContextBuilder sslcontext = new SSLContextBuilder();
sslcontext.loadTrustMaterial(null, new TrustSelfSignedStrategy());
httpclient = HttpAsyncClients.custom().setSSLContext(sslcontext.build()).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
After trying most of the solution suggested on this page and other related stackoverflow discussions, I found AJC's response above works with apache httpclient version 4.5.
Reason:
While creating SSLConnectionSocketFactory if the HostVerifier is not specified in the constructor, it does not get set and the DefaultHostVerifier is used. So line 3 of AJC's solutionmakes the difference.
(Atleast this is the behavior in apache httpclient 4.5.3 )

Securing a connection checking SSL Certificate?

I'm doing an internships.
Here they give me a code of an Android application that have been revisione by an agency for the code security and told me to change some points present in a document.
Now they fear for the information leakage because the application doesn't check for the SSL certificates when connecting to the bank server, risking a "Man In The Middle" attack.
Is there any class that i can use to check expirency date of the cert. or if it is trusted?
An example of a http connection in the app:
trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new CustomSSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams bhttpparams = new BasicHttpParams();
HttpProtocolParams.setVersion(bhttpparams, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(bhttpparams, "utf-8");
bhttpparams.setBooleanParameter("http.protocol.expect-continue", false);
HttpConnectionParams.setConnectionTimeout(bhttpparams, 20000);
HttpConnectionParams.setSoTimeout(bhttpparams, 200000);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(bhttpparams, registry);
client = new DefaultHttpClient(ccm, bhttpparams);
client.getCredentialsProvider().setCredentials(new AuthScope(null, -1), new UsernamePasswordCredentials("", ""));
HttpResponse response = client.execute(urlws);
in = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"), 8);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
The vulnerability is here. Remove it.
But I would question both the competence and the sanity of anyone who gave this task to an uninformed intern without a proper briefing, and you can tell them I said so. I would also be asking some severe questions about how this line of code ever got there in the first peace, and about the inadequacy of the testing that allowed it to remain.

Apache HTTPClient does not make more than 2 connections

I've been trying to implement connection pooling for my application using Apache HTTPClient (v4.1). The problem is that the client always makes only two connections when run, though there are enough threads running parallel. I have been trying to modify the code for a while now, but nothing has helped yet.
I'm using ThreadSafeClientConnManager for connection pooling and set the MaxTotal and DefaulMaxPerRoute to values I want.
Is there anything that comes to your mind first that I might want to check?
Here's that code segment that I use to create the client.
DefaultHttpClient createClient() {
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, new Integer(60000));
params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, new Integer(60000));
params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", sf, 6443));
registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params, registry);
cm.setMaxTotal(2 * maxConnections);
cm.setDefaultMaxPerRoute(maxConnections);
HttpHost localhost = new HttpHost("localhost");
cm.setMaxForRoute(new HttpRoute(localhost), maxConnections);
HttpHost sdpTargetHost = new HttpHost("webserviceIP", webservicePort, "https");
cm.setMaxForRoute(new HttpRoute(sdpTargetHost, null, true), maxConnections);
return new DefaultHttpClient(cm, params);
}
The client returned by this function is used in Runnables managed by a ThreadPoolExecutor. The Runnables use the client, and has these lines:
HttpResponse response = httpClient.execute(httpPost, context);
HttpEntity entity = response.getEntity();
....
EntityUtils.consume(entity);
From what I know, the EntityUtils.consume(entity) will notify the connection manager that the connection is no longer used, and thus will release the connection to be used by other threads. So I'm guessing the connection management is alright.
I guess I've provided enough info, please tell me if I'm to add anything more.
Thanks
OK. I've found the solution, thanks to oleg for pointing out the logging, and to google and all the forums. All I had to do was define the class with only the connection manager, and then set HttpParams using HttpClient.setParams(). So the code will look something like this:
DefaultHttpClient createClient() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", sf, 6443));
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(registry);
cm.setMaxTotal(maxConnections);
cm.setDefaultMaxPerRoute(maxConnections);
HttpHost targetHost = new HttpHost("webserviceIP", webservicePort, "https");
cm.setMaxForRoute(new HttpRoute(targetHost, null, true), maxConnections);
return new DefaultHttpClient(cm);
}
And right before using the client,
DefaultHttpClient httpClient = createClient();
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, new Integer(60000));
params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, new Integer(60000));
params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true);
httpClient.setParams(params);
There apparently is no difference in the code logically, but this fixed my problem. I presume this probably is some sorta bug in the HttpClient 4.1 API.
I was not able to set this option in cm
cm.setDefaultMaxPerRoute(maxConnections);
It was necessary to do so:
ConnPerRoute perRoute = new ConnPerRouteBean(100);
ConnManagerParams.setMaxConnectionsPerRoute(params, perRoute);
ConnManagerParams.setMaxTotalConnections(params, 100);
ConnManagerParams.setTimeout(params, 15000);

Http authentication with apache httpcomponents

I am trying to develop a java http client with apache httpcomponents 4.0.1. This client calls the page "https://myHost/myPage". This page is protected on the server by a JNDIRealm with a login form authentication, so when I try to get https://myHost/myPage I get a login page. I tried to bypass it unsuccessfully with the following code :
//I set my proxy
HttpHost proxy = new HttpHost("myProxyHost", myProxyPort);
//I add supported schemes
SchemeRegistry supportedSchemes = new SchemeRegistry();
supportedSchemes.register(new Scheme("http", PlainSocketFactory
.getSocketFactory(), 80));
supportedSchemes.register(new Scheme("https", SSLSocketFactory
.getSocketFactory(), 443));
// prepare parameters
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, "UTF-8");
HttpProtocolParams.setUseExpectContinue(params, true);
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params,
supportedSchemes);
DefaultHttpClient httpclient = new DefaultHttpClient(ccm, params);
httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
proxy);
//I add my authentication information
httpclient.getCredentialsProvider().setCredentials(
new AuthScope("myHost/myPage", 443),
new UsernamePasswordCredentials("username", "password"));
HttpHost host = new HttpHost("myHost", 443, "https");
HttpGet req = new HttpGet("/myPage");
//show the page
ResponseHandler<String> responseHandler = new BasicResponseHandler();
String rsp = httpClient.execute(host, req, responseHandler);
System.out.println(rsp);
When I run this code, I always get the login page, not myPage. How can I apply my credential parameters to avoid this login form?
Any help would be fantastic
HttpClient doesn't support form login. What you are trying to do is Basic Auth, which does't work with form login.
You can simply trace the form post for login page and send the POST request from HttpClient.

Categories