Chilkat HTTP for Android: Load System Root Certificates

Sean McKeon edited this page Feb 25, 2016 · 2 revisions

Here's how I setup ckHttp in my app. There are probably still some more improvements I could make. I only want to load up all the root certificates once, so I am using the Singleton pattern. I followed this guide for setting up a Singleton in Android: http://www.devahead.com/blog/2011/06/extending-the-android-application-class-and-dealing-with-singleton/

MyApp.java

First, I created a new class that extends Application. This is needed to initialize the singleton.

package com.myapp;

import android.app.Application;

import com.myapp.ssl.CkHelper;
import com.myapp.singletons.CkTrustStore;

/**
 * Created by seanmckeon on 2/22/16.
 */
public class TCSApplication extends Application {

    @Override
    public void onCreate(){
        super.onCreate();
        initSingletons();
    }

    protected void initSingletons(){
        CkTrustStore.initInstance(CkHelper.getCustomCertificates(getApplicationContext()));
    }

}

CkTrustStore.java

CkTrustStore is a Singleton class I created that will handle loading the root certificates into a CkTrustedRoots object. In my case I also needed to include a custom certificate in addition to the system root certs, so I allow initInstance to accept a list of custom certificates. The system root certificates will be loaded followed by any custom certificates you pass in.

package com.myapp.singletons;

import com.chilkatsoft.CkByteData;
import com.chilkatsoft.CkCert;
import com.chilkatsoft.CkJavaKeyStore;
import com.chilkatsoft.CkTrustedRoots;
import com.myapp.ssl.CkHelper;

import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.List;

import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

/**
 * Created by seanmckeon on 2/22/16.
 */
public class CkTrustStore {

    private static CkTrustStore instance;

    private List<CkCert> customCertificates;

    private CkTrustedRoots ckTrustedRoots;

    public static void initInstance(List<CkCert> customCertificates){
        if(instance==null){
            instance = new CkTrustStore(customCertificates);
        }
    }

    public static CkTrustStore getInstance(){
        return instance;
    }

    private CkTrustStore(List<CkCert> customCertificates){
        this.customCertificates = customCertificates;
    }

    private CkTrustedRoots buildTrustedRootsWithCertificates() throws Exception {
        CkTrustedRoots ckTrustedRoots = new CkTrustedRoots();
        boolean success = ckTrustedRoots.AddJavaKeyStore(getUnlockedKeyStoreWithCertificates());
        if(!success){
            throw new Exception("Error building trusted roots with CkJavaKeyStore" + ckJavaKeyStore.lastErrorText());
        }
        ckTrustedRoots.Activate();
        return ckTrustedRoots;
    }

    private CkJavaKeyStore getUnlockedKeyStoreWithCertificates() throws Exception {
        CkJavaKeyStore ckJavaKeyStore = CkHelper.getUnlockedKeyStore();
        ckJavaKeyStore = loadSystemCertificates(ckJavaKeyStore);
        ckJavaKeyStore = loadCustomCertificates(ckJavaKeyStore);
        return ckJavaKeyStore;
    }

    private CkJavaKeyStore loadCustomCertificates(CkJavaKeyStore ckJavaKeyStore){
        if(customCertificates != null && customCertificates.size() > 0) {
            for (CkCert ckCert : customCertificates) {
                ckJavaKeyStore.AddTrustedCert(ckCert, ckCert.issuerDN());
            }
        }
        return ckJavaKeyStore;
    }

    private CkJavaKeyStore loadSystemCertificates(CkJavaKeyStore ckJavaKeyStore) throws TrustException {
        try {
            TrustManagerFactory tmf = TrustManagerFactory
                    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init((KeyStore) null);
            X509TrustManager xtm = (X509TrustManager) tmf.getTrustManagers()[0];
            for (X509Certificate certificate : xtm.getAcceptedIssuers()) {
                CkByteData ckByteData = new CkByteData();
                ckByteData.appendByteArray(certificate.getEncoded());
                CkCert ckCert = new CkCert();
                ckCert.LoadFromBinary(ckByteData);
                if(!ckJavaKeyStore.AddTrustedCert(ckCert, certificate.getSubjectDN().getName())){
                    throw new Exception("Error trusting ckCert" + ckJavaKeyStore.lastErrorText());
                }
            }
        } catch (Exception e){
            throw new TrustException("Error loading CkJavaKeyStore store", e);
        }
        return ckJavaKeyStore;
    }

    public CkTrustedRoots getCkTrustedRoots() throws TrustException {
        if(ckTrustedRoots==null){
            ckTrustedRoots = buildTrustedRootsWithCertificates();
        }
        return ckTrustedRoots;
    }

}

CkHelper.java

This is a helper class that I do all of my UnlockComponents from. It also has a couple methods that I use to build a list of CkCert objects from local resources.

package com.myapp.ssl;

import android.content.Context;

import com.chilkatsoft.CkByteData;
import com.chilkatsoft.CkCert;
import com.chilkatsoft.CkHttp;
import com.chilkatsoft.CkJavaKeyStore;
import com.myapp.generated.R;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by seanmckeon on 2/22/16.
 */
public class CkHelper {

    private static final String TRIAL_UNLOCK_CODE = "Anything for 30-day trial";

    public static List<CkCert> getCustomCertificates(Context ctx){
        List<CkCert> customCertificates = new ArrayList<CkCert>();
        customCertificates.add(buildCkCertWithResource(ctx, R.raw.my_custom_ca));
        return customCertificates;
    }

    private static CkCert buildCkCertWithResource(Context ctx, int resourceId){
        CkCert ckCert = new CkCert();
        ckCert.LoadFromBinary(getResourceData(ctx, resourceId));
        return ckCert;
    }

    private static CkByteData getResourceData(Context ctx, int resourceId){
        InputStream inputStream  = ctx.getResources().openRawResource(resourceId);
        CkByteData ckByteData = new CkByteData();
        ckByteData.appendStr(inputStream.toString());
        return ckByteData;
    }

    public static CkHttp getUnlockedHttp() throws Exception {
        CkHttp http = new CkHttp();
        if (!http.UnlockComponent(TRIAL_UNLOCK_CODE)) {
            throw new Exception("CkHttp is locked... check chilkat unlock code");
        }
        return http;
    }

    public static CkJavaKeyStore getUnlockedKeyStore() throws Exception {
        CkJavaKeyStore ckJavaKeyStore = new CkJavaKeyStore();
        if (!ckJavaKeyStore.UnlockComponent(TRIAL_UNLOCK_CODE)) {
            throw new Exception("CkJavaKeyStore is locked... check chilkat unlock code");
        }
        return ckJavaKeyStore;
    }

    static {
        // IMPORTANT: If one of the Chilkat subset shared libs is used, the name
        // passed to loadLibrary must match the share lib name.  For example, if the
        // shared lib is libchilkatcrypt.so, then pass "chilkatcrypt" to System.loadLibrary.

        System.loadLibrary("chilkathttp");
    }
}

Using the Singleton

Here's how I use it. I'm posting XML in my case.

String postBody = "YOUR POST XML / JSON / WHATEVER";
String postUrl = "https://yourdomain.com";
Url url = new Url(postUrl);

CkHttpRequest ckHttpRequest = new CkHttpRequest();
ckHttpRequest.put_HttpVerb("POST");
ckHttpRequest.put_Path(url.getPath());
ckHttpRequest.LoadBodyFromString(postBody, "UTF-8");
ckHttpRequest.put_ContentType("text/xml");

CkHttp http = CkHelper.getUnlockedHttp();
CkTrustStore.getInstance().getCkTrustedRoots();
http.put_RequireSslCertVerify(true);
http.CloseAllConnections();
http.SynchronousRequest(host, port, true, ckHttpRequest);
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.