Overview

The basic need behind SSL in general, is that the client needs to be sure that the server is honest about who it claims it is. That is, if the server says it is domain.com, the client needs to be sure (to trust) that this is a true claim.
The server is responsible for providing the evidence. It should provide the client with a trust certificate. The client then verifies this certificate and continue with the communication or discard it.
For the server to get a trust certificate, it requests one from some certificate authority (CA).
Figure 1

Certificate Authority (CA)

CAs could be of two type:

  • Root CA, that is they do not need others to verify there trustworthy.
  • Intermediate CA, that needs to prove there trustworthy.

Intermediate CAs prove there trustworthy by being certified by other root CA.
The implications of this is that intermediate CA needs to provide a certificate from a root CA ( or another intermediate CA that has a certificate from a root CA).
Figure 2

Certificate Chain

For the client to trust an SSL server, the server should provide it with:

  1. The certificate intermediate CA 1 provided for server
  2. The certificate intermediate CA 2 provided for intermediate CA 1.
  3. The certificate root CA provided for intermediate CA 1.

That is, the client needs to trust the server, so the server provides the CA2 cert., But the client doesn't know CA2. So, the server provides the cert. of CA2 issued by CA1. Again, the client does not know CA1. And again the server provides the cert. of CA1 issued by RootCA. And because the client knows (trust) RootCA, it then trusts the whole chain of CAs down to the server.
Any Successfull SSL communication should contain all the certificate chain up to the root CA.
The following is a screenshot of an SSL connection using openssl on linux:
Figure 3

The command openssl s_client -connect rg-api.wstars.com:443 opens an SSL connection with the given domain and prints out the certificates it has. Note the marked rectangle with the "Certificate Chain" title. You will notice the following: (note thate "i" means issuer and "s" means subject)

  • There are two certificates ( with indices 0 & 1)
  • The first one (index 0) is the certificate issued by CA called "PositiveSSL" for the domain "rg-api.wstars.com"
  • The second one (index 1) is the certificate issued by CA called "AddTrust".

We conclude from this that the "PositiveSSL" CA is an intermediate CA that proves it's trustworthy with a certificate from "AddTrust" which is a root CA that does not need to provide a trustworthy proof (otherwise this will be an endless process).
From here, it is the responsibility of the client to trust or not trust the Root CA. How is that you ask, by maintaining a list of trusted Root CAs. This is were the android part of the article title kicks in.

Trusting a CA in Android

Android as an OS maintains a list of trusted root CAs. When using the HttpsUrlConnection to communicate with an SSL server, Android handles the verifying of the certificates using the maintained list of trusted CAs. The problem arises from the fact that this list of certificates is updated when the system is updated. That is, if your server uses a CA that is not yet listed, the connection will be automatically refused. Prior to Android 4.0 this list was not up-to-date, but it is very comprehensive in Android 4.0+. So, if you don't need to support older versions of Android, don't bother reading further than this.
To work around this issue, we need to maintain our list of root CAs. Fortunately, we only need to maintain the CA that our server uses to prove its trustworthy.
By maintaining a list of trusted root CAs, we mean actually; maintaining a list of trusted root CAs' certificates.

How To

The idea is to put the Root CA certificate (self-issued) in a keystore and use it as your trusted certificates store in your application.

Identifying the Root CA

Executing the following command:

openssl s_client -connect yourdomain.com:443  

will output something like the following:

CONNECTED(00000003)  
depth=2 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root  
verify return:1  
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = PositiveSSL CA 2  
verify return:1  
depth=0 OU = Domain Control Validated, OU = PositiveSSL, CN = yourdomain.com  
verify return:1  
---
Certificate chain  
 0 s:/OU=Domain Control Validated/OU=PositiveSSL/CN=yourdomain.com
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=PositiveSSL CA 2
 1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=PositiveSSL CA 2
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
---

Following the down the 'Certificate chain' you will find the root CA certificate at the end of the chain. In our case it's "AddTrust External CA Root".
For convenience feel free to use the attached bash script to create a keystore with the needed certificate. refer to the attachments section of the document.

Finding the Certificate of the Root CA

When you request an SSL certificate from some CA, it will provide you with all the certificate chain. If not, the Root CA generally make there Certificates publicly available. That is, a simple search on there site will lead you to it. Once you found it, download it.

Store the Certificate in a Keystore

The android keystore is a file that you can store certificates in it. The file has a specific format that you need to use to add your certificate to it. The Android System uses the bouncycastle format. download the latest release of there provider from their site.
Use the following command to create a keystore with the bouncycastle format that contains your Root CA certificate:

keytool -importcert -v -trustcacerts -file "path/to/certificate/file.cert" -alias "some_alias_u_choose" -keystore "keystore_file_name" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path/to/bouncycastle/jar/file" -storetype BKS -storepass "keystore_password"  

Now you have a keystore file loaded with the Root CA certificate. To be sure the keystore is created successfully and contains the desired certificate, run the following command:

keytool -v -list -keystore "keystore_file" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path/to/bouncycastle/jar/file" -storetype BKS -storepass "keystore_password"  

Use the KeyStore Inside the Android Application

Assuming you've put the keystore file in your raw directory with the name 'ssl_keystore',

protected SSLSocketFactory createSSLSocketFactory() {

    InputStream in = null;
    try {
        KeyStore localTrustStore = KeyStore.getInstance("BKS");
        in = getResources().openRawResource(R.raw.ssl_keystore);
        localTrustStore.load(in, "keystore_password".toCharArray());
        return new SSLSocketFactory(localTrustStore);
    } catch (Exception e) {
        throw new RuntimeException("Could not create SSLSocketFactory", e);
    } finally {
        if ( in != null ) {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

This Method will return an SSLSocketFactory object that is loaded with the trusted certificates loaded in the keystore. You can now use this SSLSocketFactory in your connections.