Android and HTTPS calls - android

I have developed an Android app and it is running smooth. Not yet into production. This app communicates with the server using REST services. Now
arrived an issue. As of now I am using HTTP to communicate with the server and get the info back from the server.
I wish to have the data transmission more secured. Can anyone please shed some light on how to accomplish this? I am clueless about HTTPS and implementing it in Android.
Any pointers and links will be helpful like how to start. Do I need to purchase certificates and get it signed.
Thanks

I fairly certain you don't need to do anything special for HTTPS in Android, just change your url to start with "https". new URL(url).openConnection() should work fine with that. If you're using Apache's HttpClient stuff, you might need to define your own client. Below is code I have from a project I'm currently working on.
public static DefaultHttpClient makeHTTPClient() {
BasicHttpParams mHttpParams = new BasicHttpParams();
// Set the timeout in milliseconds until a connection is established.
// The default value is zero, that means the timeout is not used.
int timeoutConnection = 15000;
HttpConnectionParams.setConnectionTimeout(mHttpParams, timeoutConnection);
// Set the default socket timeout (SO_TIMEOUT)
// in milliseconds which is the timeout for waiting for data.
int timeoutSocket = 20000;
HttpConnectionParams.setSoTimeout(mHttpParams, timeoutSocket);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
final SSLSocketFactory sslSocketFactory = SSLSocketFactory.getSocketFactory();
sslSocketFactory.setHostnameVerifier(SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
registry.register(new Scheme("https", sslSocketFactory, 443));
ClientConnectionManager cm = new ThreadSafeClientConnManager(mHttpParams, registry);
DefaultHttpClient defaultHttpClient = new DefaultHttpClient(cm, mHttpParams);
return defaultHttpClient;
}

Related

Android SSL httpGet error 500 server denied the URL

I am trying to start a connection with a microsoft exchange server web access portal. The idea is my program initially opens a web view, and then continually refreshes the page using a BroadcastReceiver. I am aware that ordinarily, exchange web-based email access can be done through a protocol, but I'm trying this anyhow. The timing for the refresh works fine, but the problem is that although I open the connection with a WebView, I'm then doing the refresh with an HttpClient. I am passing the cookies after login from the WebView to the HttpClient successfully, and opening the connection for SSL, yet the web server is kicking back. This works fine in a browser, so I'm kinda lost.
EDIT: Ok, this is a more basic HTTP request question. All of the code that handles the httpGet is setup in the constructor. I don't even make a new HttpClient. Now, This is a BroadcastReceiver, and it responds to a timed message every 60 seconds or so to make the request, but every time the code finishes, the thread seems to die, and every time the timer goes off, the blank constructor is called. When coding an http GET request like this, do I need to be recreating the HttpClient every time? or is my using static variables that hold their value an ok way of doing it? The URL and cookies survive from one call to the next. What else am I missing? The very first call to this thing doesn't seem to work either, so I still think it's that I'm doing SSL wrong. Again, this site works just fine in a web browser, so I'm not emulating the request properly.
This is the constructor code:
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
// RESTORE THE COOKIES!!!
cookieJar = new BasicCookieStore();
localContext = new BasicHttpContext();
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieJar);
cookie = extras.getString("cookies");
String[] cookieCutter = cookie.split(";");
for (int i=0; i < cookieCutter.length; i++)
{
String[] values = cookieCutter[i].split("=");
BasicClientCookie c = new BasicClientCookie(values[0], values[1]);
c.setDomain(MAIL_WEB_SERVER);
cookieJar.addCookie((Cookie)c);
}
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
// Create local HTTP context
SchemeRegistry registry = new SchemeRegistry();
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", socketFactory, 443));
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(params, registry);
httpClient = new DefaultHttpClient(mgr, params);
httpClient.setCookieStore(cookieJar);
// Set verifier
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
And this is the refresh code:
BasicCookieStore cookies = (BasicCookieStore)localContext.getAttribute(ClientContext.COOKIE_STORE);
HttpGet httpGet = new HttpGet(url);
HttpResponse response = httpClient.execute(httpGet, localContext);
So where does this leave me? I get a Error Code: 500 Internal Server Error. The server denied the specified Uniform Resource Locator (URL). Contact the server administrator. (12202)
I am moving the cookies over, and accepting all certificates...is there anything else from a browser session with an SSL connection and a login that I need to be passing along for the httpGet request?

Handle multiple HttpClient instances

I use connection for server connections as show bleow.
HttpParams params = new BasicHttpParams();
ConnManagerParams.setMaxTotalConnections(params, 10);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, Integer.valueOf(60000));
params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, Integer.valueOf(60000));
params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true);
//eate and initialize scheme registry
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// Create an HttpClient with the ThreadSafeClientConnManager.
// This connection manager must be used if more than one thread will
// be using the HttpClient.
cm = new ThreadSafeClientConnManager(params, schemeRegistry);
httpClient = new DefaultHttpClient(cm, params);
ThreadSafeClientConnManager API says:
ThreadSafeClientConnManager maintains a maximum limit of connection on a per route
basis and in total. Per default this implementation will create no more than than
2 concurrent connections per given route and no more 20 connections in total. For many
real-world applications these limits may prove too constraining, especially if they use
HTTP as a transport protocol for their services. Connection limits, however, can be
adjusted using HTTP parameters.
How can I change the maximum connections per host to more than 2?
Thanks in advance!

Android HttpClient Digest Authentication Authorization header "nc" hard-coded

We try to send several requests to target server in one HttpClient (one session). The target server will first authenticate all requests with digest authentication (based on MD5-sess). The result shows that only first access is successful. The following accesses are rejected by server because server treats later accesses as replay attack as the "nc" value is always "00000001".
It seems Android HttpClient hard-coded digest authorization header attirbute "nc" to "00000001"?
Any way for client to increase this value when new request is sent? Thanks.
public class HttpService {
private static final HttpService instance = new HttpService();
private HttpService() {
client = getHttpClient();
}
public static HttpService getInstance() {
return instance;
}
private DefaultHttpClient getHttpClient() {
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setStaleCheckingEnabled(params, false);
HttpConnectionParams.setConnectionTimeout(params, 15 * 1000);
HttpConnectionParams.setSoTimeout(params, 15 * 1000);
HttpConnectionParams.setSocketBufferSize(params, 8192);
HttpProtocolParams.setUserAgent(params, USER_AGENT);
SchemeRegistry schemeRegistry = new SchemeRegistry();
Scheme httpScheme = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
Scheme httpsScheme = new Scheme("https", SSLCertificateSocketFactory.getHttpSocketFactory(30 * 1000, null), 443);
schemeRegistry.register(httpScheme);
schemeRegistry.register(httpsScheme);
ClientConnectionManager manager = new ThreadSafeClientConnManager(params, schemeRegistry);
//create client
DefaultHttpClient httpClient = new DefaultHttpClient(manager, params);
httpClient.getCredentialsProvider().setCredentials(new AuthScope(address, port),
new UsernamePasswordCredentials(username, password));
}
}
Android ships with an extremely outdated (pre-BETA) fork of Apache HttpClient. There has been a countless number of changes in the stock version of HttpClient since 4.0 ALPHA, also in the Digest auth area.
The best thing you can do is to copy the DigestScheme from the stock version of Apache HttpClient and configure your application to use the copy instead of the default implementation.
https://github.com/apache/httpcomponents-client/blob/master/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/DigestScheme.java
For that end you will have to register a custom DigestSchemeFactory instance with the auth scheme registry.
https://github.com/apache/httpcomponents-client/blob/master/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/DigestSchemeFactory.java
It seems you are right. This is from Digest.java:
//TODO: supply a real nonce-count, currently a server will interprete a repeated request as a replay
private static final String NC = "00000001"; //nonce-count is always 1
You should file a bug at http://b.android.com. You have a few options:
calculate the digest and create the header yourself, then set it on every request. You could add a HttpRequestInterceptor to make this authomatic (don't set the credentials to the credential provider)
AFAIK authentication schemes should be pluggable, so implement digest auth properly and configure your client to use it.
use another HTTP client library.
EDIT: Since it's fixed in the stock version, there is another alternative:
Another alternative:
change the Apache HttpClient package name using jarjar, package it with the app, and don't use the Android system one at all.

Is creating HttpClient expensive?

I had seen a few examples in Android implement maybeCreateHttpClient(), especially in Google sample, which it does is to check if HttpClient object is instantiated, if not, instantiate it, and reuse it.
Why this approach? Is creating new HttpClient expensive?
Use a connection pool (eg. ThreadSafeClientConnManager) to manage your clients.
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
HttpProtocolParams.setUseExpectContinue(params, true);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
ClientConnectionManager connman = new ThreadSafeClientConnManager(params, registry);
client = new DefaultHttpClient(connman, params);
client is a singleton object which can be reused, to fire a request:
HttpResponse response = client.execute(new HttpGet(URL));
A HttpClient is fairly large and complex object. You might make tens (or probably more) http calls in your app. If you created a new one for each http request you would quickly run into memory problems and would certainly notice garbage collection slow downs.
The http client is a generic component to help you make http calls, just reuse it. There is no reason not too.

android: how to accept CA certificate

I am trying to make a secure connection to a OCS server through https in android.
I found the EasySSLFactory and EasyX509TrustManager classes to make android trust the certificate but I don't know how to initialize only one time the EasySSLFactory and EasyX509TrustManager objects.
I have the following code to accept a certificate and make a single connection:
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(),
443));
HttpParams params = new BasicHttpParams();
params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 3);
params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE,
new ConnPerRouteBean(1));
params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, "utf8");
int timeoutConnection = 1000;
HttpConnectionParams.setConnectionTimeout(params, timeoutConnection);
int timeoutSocket = 1000;
HttpConnectionParams.setSoTimeout(params, timeoutSocket);
clientConnectionManager = new ThreadSafeClientConnManager(params,
schemeRegistry);
HttpClient client = new DefaultHttpClient(clientConnectionManager,
params);
In order to make a new connection in an new method, I have to do write those lines too...
Is there a way that I can put them in the class constructor and then do connections in that class without writing that before the connection..
Thank you
Look at my blog article. I've posted a detailed description how you can add your desired certificate to a custom keystore and initialize the HttpClient with it.
Hope this helps
EDIT: I havent tried it, but maybe the TrustStrategy interface may help.
You could implement your own TrustStrategy interface and initialize the SSLSocketFactory with the appropriate constructor. Your strategy can just return true (in the isTrusted method), but you should do for security reasons a bit of checking to be sure if the certificate can be considered as trusted (it depends on your needs)
Look at line 35 on my blog article of the SecureHttpClient class. Replace the line with something like this:
SSLSocketFactory sf = new SSLSocketFactory(myTrustStrategy);
Please let me know if this works for you.
Regards

Categories

Resources