3

There is very little info about AndroidHttpClient, specifically I can't find any good examples. From what I read - I can use this client and it is preconfigured for SSL. I target 2.2+ so it will work for me well.

  1. Is there any good sample on how I use it? Specifically for REST service POST
  2. Is there any sample on how to allow self-signed certificate? I don't mind just allow ANY certificate vs importing specific ones into local store.

Thanks!

My own answer (see code below).

  1. I have IIS server with self-signed certificate. I had to go extra step and generate certificate that matches external name, not server name.
  2. I use AndroidHttpClient. Supposedly, this client has all "proper" settings for android and supported starting in version 8
  3. I create AndroidHttpClient in Application object and share across.
  4. I separated code where I inject custom certificate so it is easy to get rid of it later. I noticed it does take time on App startup to load certificate from resources.

My version of Application singleton. See comments on top with details on command lines I used to generate all the stuff. Use same password throughout to make sure it works. PKS file password have to match.

import android.net.http.AndroidHttpClient;
import android.app.Application;
import android.util.Log;
import idatt.mobile.android.providers.DBLog;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;

import java.io.InputStream;
import java.security.KeyStore;

/*
To generate PKS:
1. Created cert in IIS7 and then exported as pfx. Follow instruction on SelfSSL: http://www.robbagby.com/iis/self-signed-certificates-on-iis-7-the-easy-way-and-the-most-effective-way/
1a. Download tool: http://cid-3c8d41bb553e84f5.skydrive.live.com/browse.aspx/SelfSSL
1b. Run: SelfSSL /N:CN=mydomainname /V:1000 /S:1 /P:8081
 I use port 8081 on my server
1c. Export from IIS manager to cert.pfx
2. Run command line in SSL to convert file into X.509:
openssl pkcs12 -in C:\cert.pfx -out C:\cert.cer -nodes
3. Edit file and delete all except -----BEGIN.... END CERTIFICATE----- IMPORTANT! It was working when I got proper (5) amount of dashes and put tags and data on separate lines
4. use keytool. C:\Java\JDK\bcprov.jar was downloaded separately
 C:\Users\Ivan>keytool -import -v -trustcacerts -alias key_alias -file C:\cert.cer -keystore C:\mystore.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath C:\Java\JDK\bcprov.jar -storepass 123456

*/

public class MyApplication extends Application
{
    private static final String LOG_TAG = "MyApplication";
    private AndroidHttpClient androidHttpClient;

    @Override
    public void onCreate()
    {
        super.onCreate();
        androidHttpClient = createAndroidHttpClient();
    }

    @Override
    public void onLowMemory()
    {
        super.onLowMemory();
        shutdownAndroidHttpClient();
    }

    @Override
    public void onTerminate()
    {
        super.onTerminate();
        shutdownAndroidHttpClient();
    }


    private AndroidHttpClient createAndroidHttpClient()
    {
        Log.d(LOG_TAG,"createAndroidHttpClient");

        AndroidHttpClient client = AndroidHttpClient.newInstance("Android");

        //This is optional call to inject custom BKS that was created from self-signed certificate
        client = addCustomCertificate(client);

        return client;
    }

    public AndroidHttpClient getAndroidHttpClient()
    {
        return androidHttpClient;
    }

    private void shutdownAndroidHttpClient()
    {
        if(androidHttpClient!=null && androidHttpClient.getConnectionManager()!=null)
        {
            androidHttpClient.getConnectionManager().shutdown();
        }
    }

    private AndroidHttpClient addCustomCertificate(AndroidHttpClient client)
    {
        SSLSocketFactory sf = SSLSocketFactory.getSocketFactory();

        try
        {
            InputStream in = getResources().openRawResource(R.raw.home_server);

            KeyStore trustStore = KeyStore.getInstance("BKS");

            trustStore.load(in, "123456".toCharArray());
            in.close();

            sf = new SSLSocketFactory(trustStore);
            sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
        }
        catch (Exception t)
        {
            DBLog.InsertError(this, t);
        }

        //Lets register our custom factory here
        client.getConnectionManager().getSchemeRegistry().register(new Scheme("https", sf, 443));

        return client;
    }
}

Here is how I use this client(I call it in AsyncTask)

private String processPOST(String url, String requestData)
{
    String responseData = null;
    application = (MyApplication)getApplication();
    AndroidHttpClient client = application.getAndroidHttpClient();
    HttpPost request = new HttpPost(url);

    try
    {
        StringEntity entity = new StringEntity(requestData);
        entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
        request.setEntity(entity);
        ResponseHandler<String> handler = new BasicResponseHandler();
        responseData = client.execute(request, handler);
    }
    catch (Throwable e)
    {
        DBLog.InsertError(ctxt, e);
    }

    return responseData;
}

This combination seems to be 100% working on 2.2 and 2.3 devices. When I was using snippets with DefaultHttpClient I had issues with 2.3.1 timing out on requests (Nexus S)

katit
  • 17,375
  • 35
  • 128
  • 256

4 Answers4

5

You can use Apache HttpClient.

    public HttpClient getNewHttpClient() {
    try {
        KeyStore trustStore = KeyStore.getInstance("BKS");
        InputStream in = getResources().openRawResource(R.raw.mykeystore);
        try {
            trustStore.load(in, "mypassword".toCharArray());
        } finally {
            in.close();
        }

        SSLSocketFactory sf = new SSLSocketFactory(trustStore);
        sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
        return new DefaultHttpClient(ccm, params);
    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}

In web server, IIS can create self-signed certificate and export as PFX, then convert it to PEM using openssl tool, edit it to conatin only certificate, then create a keystore that contain the certificate using keytool of JDK and Bouncy Castle jar. The created keystore can be imported to your project as shown in above code.

bob
  • 2,674
  • 1
  • 29
  • 46
  • @bob Can you help me out with this? I got to the point where I have PEM. How should I edit it? And then I'm completely lost on how to proceed. I found http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html but it's jibberish to me since I'm pretty new to Java and mostly .NET developer. – katit Apr 14 '11 at 04:35
  • @katit That blog is the one I refered. PEM is test-editible and it may contain key and certificate. Just delete everything but certificate itself so that it looks like the one shown in the blog and save it. Then proceed to create a keystore using keytool and Bouncy Castle jar. Then the keystore can be built into your app. – bob Apr 14 '11 at 04:58
  • @bob I'm almost there :) Getting different error - now it complains that names don't match but this is because IIS generates self-signed certificate like that. I found another post http://www.robbagby.com/iis/self-signed-certificates-on-iis-7-the-easy-way-and-the-most-effective-way/ on how to get around that issue. So, if anybody interested I will post complete how to on AndroidHttpClient + self-signing IIS certificate – katit Apr 14 '11 at 15:45
  • @katit If your problem is about hostname verifry, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER can be used instead of SSLSocketFactory.STRICT_HOSTNAME_VERIFIER in above code. Yes, I am interested in that ;) – bob Apr 14 '11 at 21:01
  • @bob I posted my solution in main post. Thanks for your help, I will mark your answer. Will know about STRICT_HOSTNAME_VERIFIER. Now that I think about - I learned something new and it won't hurt having at least name matching on certificate. – katit Apr 15 '11 at 00:46
  • @katit I see interesting comment regarding speed to load cert/keystore. How much difference do you see? – bob Apr 15 '11 at 03:20
  • @bob On device - negligible - maybe half second. On emulator noticeable ~2 seconds. At least with my approach it happens only once. – katit Apr 15 '11 at 15:21
  • In case someone is struggling with `java.io.IOException: Wrong version of key store.` here is the code I used to make it work (unfortunately I can't remember where I found this solution :/): `String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); // create empty keyStore // Load CA from an InputStream CertificateFactory cf = CertificateFactory.getInstance("X.509"); InputStream caInput =... Certificate ca = cf.generateCertificate(caInput);{keyStore.setCertificateEntry("ca", ca);}` – GMan Jul 31 '14 at 21:45
0

I did some test with 'trust all' SSL connections: HTTPS GET (SSL) with Android and self-signed server certificate

Hope it helps :D

Community
  • 1
  • 1
Moss
  • 6,002
  • 1
  • 35
  • 40
-1

You can find an example here - the only thing that is "unusual" is that if the remote returns a 401 (?) code, you will not get it via connection.getResponseCode(), but an Exception will be thrown (perhaps for 5xx as well).

The above example runs over SSL, so https is possible. I can't tell anything about self-signed certs. I think I remember others saying that they had issues, but I am not sure.

Heiko Rupp
  • 30,426
  • 13
  • 82
  • 119
-1

Android integrated the Apache http client component. You will find a tutorial and examples if you look at the Documentation section of the link.

If you search for Apache Http Client instead of Android Http Client you will find lot of information.

EDIT: Oh - I just realize that there is a AndroidHttpClient Class in Android. Never used it. Have a look at this answer that helped me.

Community
  • 1
  • 1
FrVaBe
  • 47,963
  • 16
  • 124
  • 157