Java HttpClient authentication with smart card - java

Everyday I need to do a routine, connect to a remote address, authenticate with my smart card and then check any new information for my username at this remote address.
I would like to do this routine automatic, create a Java application that would connect to the address, authenticate with the smart card and then check if there is any new information.
I tryed, but had no success to set the HttpClient enviorment with the smartcard, I pluged the card at my machine and set the Cronjob to run, it returns the warning about having no card inserted.
The code is below:
HttpClient httpClient = new DefaultHttpClient();
// Secure Protocol implementation.
SSLContext ctx = SSLContext.getInstance("SSL");
// Implementation of a trust manager for X509 certificates
X509TrustManager tm = new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws java.security.cert.CertificateException {
// TODO Auto-generated method stub
}
#Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] arg0, String arg1)
throws java.security.cert.CertificateException {
// TODO Auto-generated method stub
}
};
ctx.init(null, new TrustManager[] { tm }, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx);
ClientConnectionManager ccm = httpClient.getConnectionManager();
// register https protocol in httpclient's scheme registry
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", 443, ssf));
HttpGet httpget = new HttpGet("http://remoteserver.com/login.seam");
HttpParams params = httpclient.getParams();
params.setParameter("param1", "paramValue1");
httpget.setParams(params);
System.out.println("REQUEST:" + httpget.getURI());
ResponseHandler responseHandler = new BasicResponseHandler();
String responseBody;
responseBody = httpclient.execute(httpget, responseHandler);
System.out.println(responseBody);
Is there any help with it? I searched a lot, but I´m not able to connect using the smart card...
Thanks in advance and sorry for my bad english.

We don't see your code about the smartcard reader.
In the first time you have to get informations from your smartcard.
Try to see how to use the smartcardio library.
Then send all information from your application to web.
I think that it can help you if I understood your problem.

Related

Android Java SSL Sockets - AllowAllHostNameVerifier

First I would like to say that I am discovering the world of SSL socket and there is not so much material online, I've went through most of the topics on StackOverflow, and I am still confused with the notions of TrustManager, KeyStore, HostNameVerfier...
So I have to code a Java client (One-Way SSL) to connect with some servers using SSL.
I am making three behaviors, the native one of Android (I belive it's if the cert is not trusted it does not process to the handshake).
A Naive one, with a custom TrustManager with empty checkServerTrusted function.
And now I want to use the HostNameVerfier to allow all the hostname.
But honestly I am a little bit lost and I've searched since days and days and there is no good material about android ssl on the internet.
This is my code so far:
Naive custom TrustManager (empty checkServerTrusted)
SSLContext sslContext = SSLContext.getInstance("SSL");
TrustManager trustManagerNaive = new X509TrustManager(){
#Override
public void checkClientTrusted(
X509Certificate[] chain,
String authType)
throws CertificateException {
// TODO Auto-generated method stub
}
#Override
public X509Certificate[] getAcceptedIssuers() {
// TODO Auto-generated method stub
return null;
}
#Override
public void checkServerTrusted(
X509Certificate[] chain,
String authType)
throws CertificateException {
// TODO Auto-generated method stub
}
};
sslContext.init(null, new TrustManager[]{trustManagerNaive}, null);
SSLSocketFactory socketFactory = (SSLSocketFactory)sslContext.getSocketFactory();
SSLSocket socket = (SSLSocket)socketFactory.createSocket(host, Integer.parseInt(port_number_et.getText().toString()));
//Native Android behavior (does not accept any untrusted certificate)
SSLSocketFactory socketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket)socketFactory.createSocket(host, Integer.parseInt(port_number_et.getText().toString()));
Here is a running example with an "untrusted" certificate:
KeyStore keyStore = KeyStore.getInstance("BKS");
InputStream in = getContext().getAssets().open(Constants.KEYSTORE_FILENAME);
keyStore.load(in, Constants.KEYSTORE_PASSWORD);
in.close();
SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore);
socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", socketFactory, 443));
BasicHttpParams basicHttpParams = new BasicHttpParams();
HttpProtocolParams.setVersion(basicHttpParams, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(basicHttpParams, HTTP.UTF_8);
ThreadSafeClientConnManager ccm = new ThreadSafeClientConnManager(basicHttpParams, registry);
httpClient = new DefaultHttpClient(ccm, basicHttpParams);
Some explanations:
Create a keystore with the untrusted certificate
Load the keystore and set it in the SSLSocketFactory
Set hostnameVerifier to ALLOW_ALL_HOSTNAME_VERIFIER (because the certificate is untrusted)
Then you can use this httpClient for http or https requests as follows:
httpClient.execute(httpGet, new BasicHttpContext());

Type SocketFactory is deprecated

My application goes to Performance review stage. So Performs team told me there are some deprecated methods are available, write latest versons.
My part of the code is :
private void workAroundReverseDnsBugInHoneycombAndEarlier(HttpClient client) {
// Android had a bug where HTTPS made reverse DNS lookups (fixed in Ice Cream Sandwich)
// http://code.google.com/p/android/issues/detail?id=13117
SocketFactory socketFactory = new LayeredSocketFactory() {
SSLSocketFactory delegate = SSLSocketFactory.getSocketFactory();
#Override
public Socket createSocket() throws IOException {
return delegate.createSocket();
}
#Override
public Socket connectSocket(Socket sock, String host, int port, InetAddress localAddress, int localPort, HttpParams params) throws IOException {
return delegate.connectSocket(sock, host, port, localAddress, localPort, params);
}
#Override
public boolean isSecure(Socket sock) throws IllegalArgumentException {
return delegate.isSecure(sock);
}
#Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
injectHostname(socket, host);
return delegate.createSocket(socket, host, port, autoClose);
}
private void injectHostname(Socket socket, String host) {
try {
Field field = InetAddress.class.getDeclaredField("hostName");
field.setAccessible(true);
field.set(socket.getInetAddress(), host);
} catch (Exception ignored) {
}
}
};
client.getConnectionManager().getSchemeRegistry().register(new Scheme("https", socketFactory, 443));
}
Here SocketFactory ,LayeredSocketFactory,SSLSocketFactory,HttpProtocolParams ... says
Multiple markers at this line
- The method getParams() from the type HttpClient is deprecated
- The method setUserAgent(HttpParams, String) from the type HttpProtocolParams is
deprecated
- The type HttpProtocolParams is deprecated
- The method getParams() from the type HttpClient is deprecated
- The method setContentCharset(HttpParams, String) from the type HttpProtocolParams is
deprecated
- The type HttpProtocolParams is deprecated
another sample deprecated
Multiple markers at this line
- The type SocketFactory is deprecated
- The type LayeredSocketFactory is deprecated
- The type SSLSocketFactory is deprecated
- The type SSLSocketFactory is deprecated
- The method getSocketFactory() from the type SSLSocketFactory is
deprecated
- The type SSLSocketFactory is deprecated
Please guide me . I am using HttpClient 4.3.jar file.
Please tell me any ideas...
The method getParams() from the type HttpClient is deprecated
Before 4.3.2 you could set the parameters to the client using the getParams() method (deprecated now), after 4.3.2 you can set the request params via the RequestConfig class using a Builder
Builder requestConfigBuilder = RequestConfig.custom();
requestConfigBuilder.setConnectionRequestTimeout(1000).setMaxRedirects(1);
and then set to the HttpMethod only (not to client like before)
HttpGet request = new HttpGet(builder.build());
request.setConfig(requestConfigBuilder.build());
The method setUserAgent(HttpParams, String) from the type HttpProtocolParams is deprecated
CloseableHttpClient httpclient = HttpClients.custom().setUserAgent("Agent").build();
The method setContentCharset(HttpParams, String) from the type HttpProtocolParams is deprecated
Use AbstractHttpEntity subclasses (BasicHttpEntity, ByteArrayEntity, EntityTemplate, FileEntity, InputStreamEntity, SerializableEntity, StringEntity)
HttpPost post = new HttpPost("http://");
post.setEntity(new StringEntity("content", "UTF-8"));
The type HttpProtocolParams is deprecated
(4.3) use configuration classes provided 'org.apache.http.config' and 'org.apache.http.client.config'
Builder requestConfigBuilder = RequestConfig.custom();
The type SocketFactory is deprecated & The type LayeredSocketFactory is deprecated
SSLContext sslcontext = SSLContexts.createSystemDefault();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
or
SSLContext sslContext = SSLContexts.custom()
.useTLS()
.loadTrustMaterial(myTrustStore)
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
then
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
You can take a look at HttpClient Tutorial (4.3.x) for more examples and explanations
I hope this helps you.

for rest easy https calls, how to accept all certs

i am trying to call the REST service using jboss rest easy in the following way
public ETTestCasePackage getPackageById(String packageId) throws PackageNotFound {
ClientRequest req = new ClientRequest("https://facebook/api");
req.header("Authorization", "Basic " + EztrackerConstants.base64AuthenticationValue);
req.pathParameter("id", packageId);
ETTestCasePackage etPackage = null;
try {
logger.info("invoking "+req.getUri());
//ProxyFactory.create
ClientResponse<ETTestCasePackage> res = req.get(ETTestCasePackage.class);
etPackage = res.getEntity();
} catch (Exception e) {
logger.debug("Not able to retrieve details for testcase package having id = " + packageId, e);
throw new PackageNotFound("Package with id " + packageId + " not found", e);
}
return etPackage;
}
but the above code obviously throw "peer not authenticated";
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
at sun.security.ssl.SSLSessionImpl.getPeerCertificates(Unknown Source)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:126)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:437)
at
I can add the respective cert to my local java security jks to solve this.
but i may run this so many machines, so cannot do that to all machines. so i want to make my http client accept all request by overridding the http checks.
but for rest easy httprequest, i am not able to find a way to do this. would some one help me in doing for this rest easy.
Thanks in Advance,
syam.
I have tried this piece of code calling the actual code for ignoring but still didn't override the default settings. any idea for to make it work for this rest easy client.
private void test(){
TrustManager[] trustAllCerts = new TrustManager[]{
new 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) {
}
}
};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
}
}
static {
//for localhost testing only
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier(){
public boolean verify(String hostname,
javax.net.ssl.SSLSession sslSession) {
return true;
}
});
}
}
Use signed certs as a plan A. As a plan B, when targeting a staging version of another system that you do not control for example, you can use the following solution.
For Resteasy 3, you need to provide your own all-trusting Httpclient to the client instance.
Of course you should never use that in production, so make sure not to hardoce it.
Normally (using jax-rs 2.0) you'd initialize a client like this:
javax.ws.rs.client.Client client = javax.ws.rs.client.ClientBuilder.newClient();
For all trusting client, replace it as follows:
Client client = null;
if (config.trustAllCertificates) {
log.warn("Trusting all certificates. Do not use in production mode!");
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(createAllTrustingClient());
client = new ResteasyClientBuilder().httpEngine(engine).build();
}
else {
client = ClientBuilder.newClient();
}
The createAllTrustingClient() would look like this:
private DefaultHttpClient createAllTrustingClient() throws GeneralSecurityException {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
TrustStrategy trustStrategy = new TrustStrategy() {
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
};
SSLSocketFactory factory = new SSLSocketFactory(trustStrategy, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER );
registry.register(new Scheme("https", 443, factory));
ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(registry);
mgr.setMaxTotal(1000);
mgr.setDefaultMaxPerRoute(1000);
DefaultHttpClient client = new DefaultHttpClient(mgr, new DefaultHttpClient().getParams());
return client;
}
Just in case you have trouble figuring out the package names of the classes, here are the relevant imports:
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
For reference:
https://docs.jboss.org/resteasy/docs/3.0-beta-3/userguide/html/RESTEasy_Client_Framework.html#transport_layer
The easiest method is to get a proper certificate, with a correct DN and signed by a public CA, on each machine on which you are deploying the service. It's bureaucratic and annoying and probably costs real money, but it is definitely easiest overall.
Otherwise, you have to configure the clients to have a verifier that doesn't actually verify. That's dangerous, since anyone at all (including random hackers, organised criminals and dodgy government agencies) can make a self-signed certificate and there's no practical way to detect that they have done so. Except by going through and distributing to every client the entire list of server certificates that will ever be used (allowing the verifier to do its check using the club doorman technique: “if you're not on the list, you're not coming in”).
The verifier is technically going to be some kind of instance of X509TrustManager.
To add up on Arnelism's answer: if you are using httpclient-4.2.6.jar (which is a dependency for resteasy-jaxrs-3.0.10.Final.jar), you will find that ThreadSafeClientConnManager is #Deprecated. You can modify it to BasicClientConnectionManager or PoolingClientConnectionManager instead:
private static DefaultHttpClient createAllTrustingClient()
throws GeneralSecurityException {
SchemeRegistry registry = new SchemeRegistry();
registry.register(
new Scheme("http", 80, PlainSocketFactory.getSocketFactory())
);
TrustStrategy trustStrategy = new TrustStrategy() {
#Override
public boolean isTrusted(java.security.cert.X509Certificate[] arg0,
String arg1) throws java.security.cert.CertificateException {
return true;
}
};
SSLSocketFactory factory = new SSLSocketFactory(
trustStrategy,
SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
);
registry.register(new Scheme("https", 443, factory));
BasicClientConnectionManager mgr = new BasicClientConnectionManager(registry);
DefaultHttpClient client =
new DefaultHttpClient(mgr, new DefaultHttpClient().getParams());
return client;
}
It's necessary to hack the ApacheHttpClient4Executor, the code below is work with HTTPS and will provide a ClientRequest:
UriBuilder uri = UriBuilder.fromUri(request.endpoint() + request.path());
System.out.println(request.endpoint() + request.path());
class ApacheHttpClient4Executor2 extends ApacheHttpClient4Executor {
}
ApacheHttpClient4Executor2 executor = new ApacheHttpClient4Executor2();
Scheme http = new Scheme("http", 80, PlainSocketFactory.getSocketFactory());
TrustStrategy trustStrategy = new TrustStrategy() {
#Override
public boolean isTrusted(java.security.cert.X509Certificate[] chain, String authType)
throws CertificateException {
return true;
}
};
SSLSocketFactory factory = null;
try {
factory = new SSLSocketFactory(trustStrategy, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
} catch (KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e1) {
e1.printStackTrace();
}
Scheme https = new Scheme("https", 443, factory);
executor.getHttpClient().getConnectionManager().getSchemeRegistry().register(http);
executor.getHttpClient().getConnectionManager().getSchemeRegistry().register(https);
ClientRequest client = new ClientRequest(uri, executor, providerFactory);

how to ignore self signed certificate and supress peer not authenticated error

I'm trying to send a Https Post request to a server that is using a self signed certificate and i'm receving an exception with the error: peer not authenticated.
I googled and found that the cause of the problem is that the server is using a self signed ceritficate. how can I supress this error ?
I'm using the following function to send the post request:
public String sendPost(final String request, final String postData) throws ClientProtocolException, IOException {
String result = null;
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(request);
ByteArrayEntity postDataEntity = new ByteArrayEntity(postData.getBytes());
httpPost.setEntity(postDataEntity);
CloseableHttpResponse response = httpclient.execute(httpPost);
try {
HttpEntity entity = response.getEntity();
result = EntityUtils.toString(entity);
EntityUtils.consume(entity);
} finally {
response.close();
}
return result;
}
what am I missing to supress this error ? I don't want to try and catch this exception.
I want to configure it properly so self signed certificate will be accepted. I'm using
Httpclient 4.1.
thank you!
Many answers that you will find to this question on the web (including ufk's answer) will work, but are not at all secure, because they completely ignore the self-signed server certificate.
This removes much of the benefit of an SSL connection, and opens you up to a man-in-the-middle attack.
What you probably want to do instead is to trust a specific self-signed server certificate, rather than blindly accepting any server certificate.
The key to this is putting a copy of the server's certificate chain into the trust store when creating the SSL context.
The code for doing this is a bit too long to post here, but as it happens, I'm currently working on a blog post about doing this on Android. The blog post isn't published yet, but the sample code is available on GitHub.
public String sendPost(final String request, final String postData) throws ClientProtocolException, IOException, NoSuchAlgorithmException, KeyManagementException {
String result = null;
SSLContext sslContext = SSLContext.getInstance("SSL");
// set up a TrustManager that trusts everything
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
System.out.println("getAcceptedIssuers =============");
return null;
}
public void checkClientTrusted(X509Certificate[] certs,
String authType) {
System.out.println("checkClientTrusted =============");
}
public void checkServerTrusted(X509Certificate[] certs,
String authType) {
System.out.println("checkServerTrusted =============");
}
} }, new SecureRandom());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(new SSLSocketFactory(sslContext)).build();
HttpPost httpPost = new HttpPost(request);
ByteArrayEntity postDataEntity = new ByteArrayEntity(postData.getBytes());
httpPost.setEntity(postDataEntity);
CloseableHttpResponse response = httpclient.execute(httpPost);
try {
HttpEntity entity = response.getEntity();
result = EntityUtils.toString(entity);
EntityUtils.consume(entity);
} finally {
response.close();
}
return result;
}

Java SSLException: hostname in certificate didn't match

I have been using the following code to connect to one of google's service. This code worked fine on my local machine :
HttpClient client=new DefaultHttpClient();
HttpPost post = new HttpPost("https://www.google.com/accounts/ClientLogin");
post.setEntity(new UrlEncodedFormEntity(myData));
HttpResponse response = client.execute(post);
I put this code in a production environment, which had blocked Google.com. On request, they allowed communication with Google server by allowing me to accessing an IP : 74.125.236.52 - which is one of Google's IPs. I edited my hosts file to add this entry too.
Still I could not access the URL, which I wonder why. So I replaced the above code with :
HttpPost post = new HttpPost("https://74.125.236.52/accounts/ClientLogin");
Now I get an error like this :
javax.net.ssl.SSLException: hostname in certificate didn't match:
<74.125.236.52> != <www.google.com>
I guess this is because Google has multiple IPs. I cant ask the network admin to allow me access to all those IPs - I may not even get this entire list.
What should I do now ? Is there a workaround at Java level ? Or is it totally in hands of the network guy ?
You can also try to set a HostnameVerifier as described here. This worked for me to avoid this error.
// Do not do this in production!!!
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
DefaultHttpClient client = new DefaultHttpClient();
SchemeRegistry registry = new SchemeRegistry();
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
registry.register(new Scheme("https", socketFactory, 443));
SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry);
DefaultHttpClient httpClient = new DefaultHttpClient(mgr, client.getParams());
// Set verifier
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
// Example send http request
final String url = "https://encrypted.google.com/";
HttpPost httpPost = new HttpPost(url);
HttpResponse response = httpClient.execute(httpPost);
The certificate verification process will always verify the DNS name of the certificate presented by the server, with the hostname of the server in the URL used by the client.
The following code
HttpPost post = new HttpPost("https://74.125.236.52/accounts/ClientLogin");
will result in the certificate verification process verifying whether the common name of the certificate issued by the server, i.e. www.google.com matches the hostname i.e. 74.125.236.52. Obviously, this is bound to result in failure (you could have verified this by browsing to the URL https://74.125.236.52/accounts/ClientLogin with a browser, and seen the resulting error yourself).
Supposedly, for the sake of security, you are hesitant to write your own TrustManager (and you musn't unless you understand how to write a secure one), you ought to look at establishing DNS records in your datacenter to ensure that all lookups to www.google.com will resolve to 74.125.236.52; this ought to be done either in your local DNS servers or in the hosts file of your OS; you might need to add entries to other domains as well. Needless to say, you will need to ensure that this is consistent with the records returned by your ISP.
I had similar problem. I was using Android's DefaultHttpClient. I have read that HttpsURLConnection can handle this kind of exception. So I created custom HostnameVerifier which uses the verifier from HttpsURLConnection. I also wrapped the implementation to custom HttpClient.
public class CustomHttpClient extends DefaultHttpClient {
public CustomHttpClient() {
super();
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier(new CustomHostnameVerifier());
Scheme scheme = (new Scheme("https", socketFactory, 443));
getConnectionManager().getSchemeRegistry().register(scheme);
}
Here is the CustomHostnameVerifier class:
public class CustomHostnameVerifier implements org.apache.http.conn.ssl.X509HostnameVerifier {
#Override
public boolean verify(String host, SSLSession session) {
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify(host, session);
}
#Override
public void verify(String host, SSLSocket ssl) throws IOException {
}
#Override
public void verify(String host, X509Certificate cert) throws SSLException {
}
#Override
public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
}
}
A cleaner approach ( only for test environment) in httpcliet4.3.3 is as follows.
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
In httpclient-4.3.3.jar, there is another HttpClient to use:
public static void main (String[] args) throws Exception {
// org.apache.http.client.HttpClient client = new DefaultHttpClient();
org.apache.http.client.HttpClient client = HttpClientBuilder.create().build();
System.out.println("HttpClient = " + client.getClass().toString());
org.apache.http.client.methods.HttpPost post = new HttpPost("https://www.rideforrainbows.org/");
org.apache.http.HttpResponse response = client.execute(post);
java.io.InputStream is = response.getEntity().getContent();
java.io.BufferedReader rd = new java.io.BufferedReader(new java.io.InputStreamReader(is));
String line;
while ((line = rd.readLine()) != null) {
System.out.println(line);
}
}
This HttpClientBuilder.create().build() will return org.apache.http.impl.client.InternalHttpClient. It can handle the this hostname in certificate didn't match issue.
Thanks Vineet Reynolds. The link you provided held a lot of user comments - one of which I tried in desperation and it helped. I added this method :
// Do not do this in production!!!
HttpsURLConnection.setDefaultHostnameVerifier( new HostnameVerifier(){
public boolean verify(String string,SSLSession ssls) {
return true;
}
});
This seems fine for me now, though I know this solution is temporary. I am working with the network people to identify why my hosts file is being ignored.
The concern is we should not use ALLOW_ALL_HOSTNAME_VERIFIER.
How about I implement my own hostname verifier?
class MyHostnameVerifier implements org.apache.http.conn.ssl.X509HostnameVerifier
{
#Override
public boolean verify(String host, SSLSession session) {
String sslHost = session.getPeerHost();
System.out.println("Host=" + host);
System.out.println("SSL Host=" + sslHost);
if (host.equals(sslHost)) {
return true;
} else {
return false;
}
}
#Override
public void verify(String host, SSLSocket ssl) throws IOException {
String sslHost = ssl.getInetAddress().getHostName();
System.out.println("Host=" + host);
System.out.println("SSL Host=" + sslHost);
if (host.equals(sslHost)) {
return;
} else {
throw new IOException("hostname in certificate didn't match: " + host + " != " + sslHost);
}
}
#Override
public void verify(String host, X509Certificate cert) throws SSLException {
throw new SSLException("Hostname verification 1 not implemented");
}
#Override
public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
throw new SSLException("Hostname verification 2 not implemented");
}
}
Let's test against https://www.rideforrainbows.org/ which is hosted on a shared server.
public static void main (String[] args) throws Exception {
//org.apache.http.conn.ssl.SSLSocketFactory sf = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory();
//sf.setHostnameVerifier(new MyHostnameVerifier());
//org.apache.http.conn.scheme.Scheme sch = new Scheme("https", 443, sf);
org.apache.http.client.HttpClient client = new DefaultHttpClient();
//client.getConnectionManager().getSchemeRegistry().register(sch);
org.apache.http.client.methods.HttpPost post = new HttpPost("https://www.rideforrainbows.org/");
org.apache.http.HttpResponse response = client.execute(post);
java.io.InputStream is = response.getEntity().getContent();
java.io.BufferedReader rd = new java.io.BufferedReader(new java.io.InputStreamReader(is));
String line;
while ((line = rd.readLine()) != null) {
System.out.println(line);
}
}
SSLException:
Exception in thread "main" javax.net.ssl.SSLException: hostname in certificate didn't match: www.rideforrainbows.org != stac.rt.sg OR stac.rt.sg OR www.stac.rt.sg
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:231)
...
Do with MyHostnameVerifier:
public static void main (String[] args) throws Exception {
org.apache.http.conn.ssl.SSLSocketFactory sf = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory();
sf.setHostnameVerifier(new MyHostnameVerifier());
org.apache.http.conn.scheme.Scheme sch = new Scheme("https", 443, sf);
org.apache.http.client.HttpClient client = new DefaultHttpClient();
client.getConnectionManager().getSchemeRegistry().register(sch);
org.apache.http.client.methods.HttpPost post = new HttpPost("https://www.rideforrainbows.org/");
org.apache.http.HttpResponse response = client.execute(post);
java.io.InputStream is = response.getEntity().getContent();
java.io.BufferedReader rd = new java.io.BufferedReader(new java.io.InputStreamReader(is));
String line;
while ((line = rd.readLine()) != null) {
System.out.println(line);
}
}
Shows:
Host=www.rideforrainbows.org
SSL Host=www.rideforrainbows.org
At least I have the logic to compare (Host == SSL Host) and return true.
The above source code is working for httpclient-4.2.3.jar and httpclient-4.3.3.jar.
Updating the java version from 1.8.0_40 to 1.8.0_181 resolved the issue.
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build(),
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();

Categories