Change code from httpClient to httpClient builder. Android API 23+ - java

I am currently struggling to prepare my code for SOAP communication with SOAP protocol behind it (no I can't use JSON). The idea is that I send a certificate for server, that tells me, if the device is allowed to connect and than we developed our system with tokens, which works quite well. I am not sure how API 23 is far, but I am sure that at one point I would need to change my network solution. Everything is working correctly right now.
So far looks like this:
private static HttpClient getNewHttpClient() {
try {
KeyStore trustStore = KeyStore.getInstance("BKS");
InputStream trustStoreStream = SampleApp.appContext.getResources().openRawResource(R.raw.server);
trustStore.load(trustStoreStream, "A123456a".toCharArray());
SSLSocketFactory sslf = new SSLSocketFactory(trustStore);
sslf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme ("https", sslf, 443));
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
HttpConnectionParams.setConnectionTimeout(params, 20000000);
HttpConnectionParams.setSoTimeout(params, 20000000);
SingleClientConnManager cm = new SingleClientConnManager(params, schemeRegistry);
return new DefaultHttpClient(cm, params);
} catch (Exception e) {
e.printStackTrace();
return new DefaultHttpClient();
}
}
And that works fine until i change in my gradle SDK 23, than all of the sudden everything was red. Can someone point me out how to solve this. This application should be secure and it's used by more 1000+ users, which are clients of private company. We've got just jet certificate of security and this happened. I want my application transmission to be as secure as possible. Can someone help me?
Passwords and names was obviously change, for sake of security. Thanks for any help. Maybe I am idiot, but Apache description of functions isn't enough.
Thanks a lot for anyone willing to help.

Related

HttpClient : hostname didn't match - accessible from browser but not from code

I am trying to access a website from my code using HttpClient :
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("https://www.datamed.org/search.php?query=gene&searchtype=data");
ResponseHandler<String> responseHandler = new BasicResponseHandler();
String responseBody = httpclient.execute(httpget, responseHandler);
This is the error i am getting :
Exception in thread "main" javax.net.ssl.SSLException: hostname in certificate didn't match: <www.datamed.org> != <ucrexdc.ucsd.edu> OR <ucrexdc.ucsd.edu>
I checked the certificate from browser, it seems correct, with correct names.
Not sure from where it is picking up ucrexdc.ucsd.edu .
The code does work if I use a proxy.
Gone through a lot of similar issues on StackOverflow, but in most cases the server was under user's control. In my case, this is an already existing website. and i have this problem only for this website.
Can it be a problem with my environment?
UPDATE:
I found out that both the websites (datamed.org and ucrexdc.ucsd.edu) have the same IP , 169.228.51.21 . Can it be a problem, why doesn't the browser have issues with this?
UPDATE 2:
I was using apache http-client 4.3.1,
When i updated to 4.4.1, it was resolved. the issue was most possibly related to SNI.
HttpClient provides two implementations for Hostname verification.
DefaultHostnameVerifier
NoopHostnameVerifier
by default HttpClient uses DefaultHostnameVerifier implementation. You can try the different hostname verifier implementation.
SSLContext sslContext = SSLContexts.createSystemDefault();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
HttpClient httpClient = HttpClientBuilder.create().setSSLSocketFactory(sslsf).build();

How to setEnabledCipherSuites when using Apache HTTP Client?

Since I need to work with some legacy server, and since RC4 was removed from the Java 8, I need to re-enable some RC4 based ciphers. As described in the release note we have to use SSLSocket/SSLEngine.setEnabledCipherSuites(). Since I'm using Apache HTTP Client I was not able to find a way to do this. Thanks in advance! (I also found quite semitrailer problem with out an answer so thought of posting a new one)
I was facing the same problem and I was able to figure this out.
SecureProtocolSocketFactoryImpl protFactory = new SecureProtocolSocketFactoryImpl();
httpsClient.getHostConfiguration().setHost(host, port, httpsProtocol);
In the "SecureProtocolSocketFactoryImpl" class you have to override the method public Socket createSocket() for SecureProtocolSocketFactory class.
In that method you will get a socket like this
SSLSocket soc = (SSLSocket) getSSLContext().getSocketFactory().createSocket(
socket,
host,
port,
autoClose
);
So there you will be able to do something like below.
ciphersToBeEnabled[0] = "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA";
soc.setEnabledCipherSuites(ciphersToBeEnabled);
hope you get the idea. If you have any problems please comment below. Note that doing this only will not enable RC4 related ciphers. You will need to modify java "java.security" file in jre/lib/security/ file and remove CR4 form the disabled algorithm list.
The recommended way to get the HttpClient is by using HttpClientBuilder. In this builder, you can set the HttpClientConnectionManager which in turn can take a Registry<ConnectionSocketFactory>. In this ConnectionSocketFactory, you can configure ciphers and protocols that the client want to restrict.
Sample Code:
Registry<ConnectionSocketFactory> socketFactoryRegistry;
{
SSLContext sslcontext = <your SSLContext>;
socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new PlainConnectionSocketFactory())
.register("https", new SSLConnectionSocketFactory(sslcontext,
<your supported protocols, could be null>,
<your supported ciphers, could be null>,
<your HostnameVerifier>
.build();
}
HttpClientBuilder b = HttpClientBuilder.create()
.setConnectionManager(new BasicHttpClientConnectionManager(socketFactoryRegistry))
.set<anything else you want>(<with what you want>);
HttpClient client = b.build();

How to make Android app connect to Heroku app over HTTPS? [duplicate]

This question already has answers here:
Https Connection Android
(11 answers)
Closed 9 years ago.
I've got a Heroku app on the Cedar stack, which has a URL like this:
https://my-app.herokuapp.com/
I'm using piggyback SSL, I don't have my own certificate. But this works fine and I'm not seeing any errors/warnings in the browser.
Now I want to let my Android app securely connect to this Heroku app. The code I tried was the following:
BasicHttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, 4000);
HttpConnectionParams.setSoTimeout(httpParameters, 4000);
DefaultHttpClient client = new DefaultHttpClient(httpParameters);
HttpRequestBase httpRequest = new HttpGet("https://my-app.herokuapp.com/api/player");
client.execute(httpRequest);
But this does not work. I'm not seeing any warnings, errors or exceptions, but it just doesn't connect over HTTPS but HTTP.
What am I doing wrong?
Are there any other subclasses that I have to use? I thought that just providing the HTTPS URL would be enough, and some posts I found on the internet seem to verify this.
I've found answers regarding HttpClient with SSL/TLS here, here and here, but they don't really help me. Apart from the fact that I don't exactly know what to do, I'm not sure if these answers affect me at all, because I'm not seeing any exceptions that hint to problems with the certificate.
You can try this:
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
DefaultHttpClient defaultclient = new DefaultHttpClient();
SchemeRegistry registry = new SchemeRegistry();
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
registry.register(new Scheme("https", socketFactory, 443));
cm = new ThreadSafeClientConnManager(defaultclient.getParams(), registry);
client = new DefaultHttpClient(cm, defaultclient.getParams());
// Set verifier
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
post = new HttpPost("https://my-app.herokuapp.com/api/player");

Base64 Encoding Basic Authentication Header Apache HTTP Client

Two related questions, I'm using Apache HTTP Client 4.x API. myHttpPost is an instance of HttpPost and myHttpClient is an instance of HttpClient. I'm trying to send a request using basic authentication. So I have a HttpClient and create a HttpPost.
The 'brute force' way of setting a basic authentication header seems to be to set it in the HttpPost header.
String encoding = Base64Encoder.encode("username" + ":" + "password");
myHttpPost.setHeader("Authorization", "Basic " + encoding);
The example above came from another stack overflow question (can't find link to it now). In relation to the Base64Encoder class - which package would I find it in or where would I download it from?
Main question - I was hoping to do basic authentication in a more aesthetic manner using the code below:
myHttpClient.getCredentialsProvider().setCredentials(
new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthPolicy.BASIC),
new UsernamePasswordCredentials("username", "password")
);
But this doesn't seem to work. So is the first example above the right way to do basic authentication with Apache HTTP Client 4.0? Or is there a cleaner/simpler way.
In relation to the Base64Encoder class - which package would I find it
in or where would I download it from?
Base64Encoder can come from various places, I couldn't find something that matches with your static encode method.
As for Credentials, you need to set scheme to Basic on your AuthScope, like so:
myHttpClient.getCredentialsProvider().setCredentials(
new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, "basic"),
new UsernamePasswordCredentials("username", "password")
);
or
myHttpClient.getCredentialsProvider().setCredentials(
new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthPolicy.BASIC),
new UsernamePasswordCredentials("username", "password")
);
I know this is a really old post. But, just wanted to answer so that others will benefit in future:
If you are using a username which is to be represented using Variable width encoding (http://en.wikipedia.org/wiki/Variable-width_encoding), then make sure that you change the encoding used to form the bytes for (username:password) as follows:
HttpParams params = new BasicHttpParams();
params.setParameter(AuthPNames.CREDENTIAL_CHARSET, HTTP.UTF_8);
By default, the encoding used is HttpProtocolParams.HTTP_ELEMENT_CHARSET.
HttpClient does not attempt to authenticate with the origin or proxy server unless explicitly challenged. I suspect you would like HttpClient to authenticate preemptively. While the preemptive authentication is disabled per default (and I personally would discourage its application outside secure or internal networks) one can force the preemptive authentication using example below
http://hc.apache.org/httpcomponents-client-ga/httpclient/examples/org/apache/http/examples/client/ClientPreemptiveBasicAuthentication.java
I tried out the solution suggested in http://hc.apache.org/httpcomponents-client-ga/httpclient/examples/org/apache/http/examples/client/ClientPreemptiveBasicAuthentication.java and it works for Base64 Encoding Basic Authentication.
I guess the
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local
// auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put(target, basicAuth);
// Add AuthCache to the execution context
HttpClientContext localContext = HttpClientContext.create();
localContext.setAuthCache(authCache);
makes the miracle :)
as without this i always receive "401 Unauthorized" HTTP response

connect over ssl without certificate

I'm trying to connect to my server over SSL port 443 without a certificate.
I'm getting an error thrown:
javax.net.ssl.SSLException: Not trusted server certificate
Reading other questions to solve the problem, the following code should work, but I'm still getting the error message. What could I be doing wrong?
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);
httpclient = new DefaultHttpClient(mgr, client.getParams());
// Set verifier
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
HttpGet httpget = new HttpGet(this.requestedURL);
httpget.addHeader(new BasicScheme().authenticate(creds, httpget));
try
{
response = httpclient.execute(httpget);
}
catch(java.lang.Throwable t) {}
Your client truststore doesn't trust the server certificate. It is probably a self-signed certificate, so you need to import it into your clients truststore. Or get it signed by a CA. Ignoring the server certificate isn't secure, you may as well not use HTTPS at all.
After trying all other solutions, android 2.2 + needs special code. This worked
Custom SSL handling stopped working on Android 2.2 FroYo

Categories

Resources