Blog maintained by TechnoGems

Tuesday, October 16, 2012

Making https connection from Android client if your CA provider is not trusted by Android

Android does not recognize all the SSL certificate issuers. If your certificate happens to be issues by one of them or you have a self signed certificate and you want your android application to talk to the server,  the code can be a bit tricky.  Android requires a special version of the keystore.  Here are 5 steps you need to perform to make it work.

1) Download the correct version of bouncycastle. For clients running API level 8, version 1.46 works fine. It can be downloaded from  http://ftp.uasw.edu/pub/security/bouncycastle/release1.46/bcmail-jdk13-146.jar

2) Extract the certificate from the server using the following command:

echo | openssl s_client -connect server.name.com:443 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem

3) Run the following command to create the keystore

keytool -importcert -v -trustcacerts -file mycert.pem  \
-alias server.name.com -keystore mykeystore.bks \
-provider org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath /path/to/your/Download/bcprov-jdk15on-146.jar  \
-storetype BKS -storepass mypass

4) Copy the file to the Android resource directory under the name res/raw/mykeystore.bks

5) Add the following code snippet in your application and have it executed before you make the https url connection.
    public void setDefaultSSL () {
        Context con = getApplicationContext() ;
        TrustManagerFactory tmf;
        try {
            tmf = TrustManagerFactory.getInstance("X509");
            KeyStore ks = KeyStore.getInstance("BKS");
            InputStream in = con.getResources().openRawResource(R.raw.mykeystore);
            ks.load(in, "mypass".toCharArray());
            in.close();
            tmf.init(ks);
            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, tmf.getTrustManagers(), null);
            HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
            Log.d("HTTPS", "Setting custom trust store");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

Once you have invoked the above code segment, you can make url connection as you normally do and you should be fine.  Here is a code snippet to do that. One point to note in this code below is that the keepAlive header. Without this, you may experience intermittent issues where the response from the call is empty.

    private String getServerContent(String url_str ) throws Exception {
        System.setProperty("http.keepAlive", "false");
        URL url = new URL(url_str);
        HttpsURLConnection urlConnection = (HttpsURLConnection) url
                .openConnection();
        urlConnection.setDoInput(true);
        InputStream ins = urlConnection.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                ins, "iso-8859-1"), 8);
        StringBuilder sb = new StringBuilder();
        String line = null;
        while ((line = reader.readLine()) != null) {
            sb.append(line + "\n");
        }
        ins.close();
        urlConnection.disconnect();
       
        return sb.toString();

    }
 

No comments: