Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AndroidHttpClient need more info. Self-signed SSL. Possible?

Tags:

android

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)

like image 200
katit Avatar asked Nov 26 '25 09:11

katit


1 Answers

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.

like image 82
bob Avatar answered Nov 28 '25 23:11

bob



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!