Wednesday, May 11, 2016

Using Self Signed Certificate With Retrofit Android

The Problem
You are developing an application with its own backend server and you want to create a secure SSL connection from your Android app with a self signed certificate

You are
  1.  Using retrofit 2
  2.  Have the self signed certificate and its root CA, if you don't then this link can help you 
You could however install the certificate manually the device but it has two drawbacks
  1. You have to do it for every device
  2. You force the user to have a pattern, pin or password lock




Solution
First we are going to add the certificate CA to the "res\raw" folder in this case the certificate is called car.crt

when you are building a normal retrofit request it is usually something like this

_retrofit = new Retrofit.Builder()
       .baseUrl(CustomService.API_URL)
       .addConverterFactory(GsonConverterFactory.create())
       .build();



we are going to change that a little bit, by doing the following 
we are going to read the certificate and then we are going to add it to a keystore same as in Android developer and then create an OkHttpClient and return it






private static OkHttpClient getHttpClient(Context context){
    try {
        // Load CAs from an InputStream        
        // (could be from a resource or ByteArrayInputStream or ...)        
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
 // get InputStream for the certificate
        InputStream caInput = context.getResources().openRawResource(R.raw.car);
        Certificate ca;
        ca = cf.generateCertificate(caInput);
        System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
        // Create a KeyStore containing our trusted CAs        

        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);
        // Create a TrustManager that trusts the CAs in our KeyStore        

        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);
        // Create an SSLContext that uses our TrustManager        

        SSLContext sslcontext = SSLContext.getInstance("TLS");
        sslcontext.init(null, tmf.getTrustManagers(), null);
        final OkHttpClient client;
        client = new OkHttpClient.Builder()
                .sslSocketFactory(sslcontext.getSocketFactory())
                .build();
        caInput.close();
        return client;
    } catch (Exception e) {
        return null;
    }
}

Now when you are building your retrofit object you are just going to add an extra line
_retrofit = new Retrofit.Builder()
       .client(getHttpClient(context))
       .baseUrl(CustomService.API_URL)
       .addConverterFactory(GsonConverterFactory.create())
       .build();
You can now use retrofit to create any request over an Https connection. please comment or contact me in case you have something to add or found any bugs.