Wednesday, September 8, 2010

Step-to-Step Guide to Programming Android SSL with Self-Signed Server Certificate

There is a dearth of SDK documentation on how to work with SSL connections on Android with self-signed certificate. Here is a method that stores a self-signed certificate in the application resource and then later uses that certificate for SSL connections.

1. We create a self-signed server certificate for our SSL server:
keytool -genkey -dname "cn=ssltest, ou=test, o=example, c=US" 
   -alias ssltest -keypass ssltest -keystore c:\test\ssltest.keystore 
   -storepass ssltest -validity 180

2. We export the certificate to a file:
keytool -export -alias ssltest -keystore c:\test\ssltest.keystore 
   -file c:\test\ssltest.cer -storepass ssltest -keypass ssltest

3. Since Android uses the JCE provider from Bouncy Castle, we download the provider jar bcprov-jdk16-145.jar from BC and store it at C:\androidproject\libs.

4. Now, we import the server certificate to our Android project as a raw resource:
keytool -import -alias ssltestcert -file C:\test\ssltest.cer 
   -keypass ssltestcert -keystore C:\androidproject\res\raw\ssltestcert 
   -storetype BKS -storepass ssltestcert 
   -providerClass org.bouncycastle.jce.provider.BouncyCastleProvider 
   -providerpath c:\androidproject\libs\bcprov-jdk16-145.jar
Note that we give it a store type BKS.
If you use the Eclipse ADK, the ADK will automatically create a resource id ssltestcert after you refresh the project.

5. We can now use the server certificate in our Java program:
// Load the self-signed server certificate
char[] passphrase = "ssltestcert".toCharArray();
KeyStore ksTrust = KeyStore.getInstance("BKS");
ksTrust.load(context.getResources().openRawResource(R.raw.ssltestcert),
             passphrase);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
tmf.init(ksTrust);

// Create a SSLContext with the certificate
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());

// Create a HTTPS connection
URL url = new URL("https", "10.0.2.2", 8443, "/ssltest");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();

/* Uncomment the following line of code if you want to skip SSL */
/* hostname verification.  But it should only be done for testing. */
/* See http://randomizedsort.blogspot.com/2010/09/programmatically-disabling-java-ssl.html */
/* conn.setHostnameVerifier(new NullVerifier()); */

conn.setSSLSocketFactory(sslContext.getSocketFactory());

Congratulations! You can now use your self-signed server certificate for SSL communication.

9 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Had to adjust a little, but worked well. Thanks for the post

    ReplyDelete
  3. Hi,
    Could you share what adjustments were needed?

    ReplyDelete
  4. Hi,
    I followed the steps mentioned above to generate the BKS keystore and am able to open it using Portecle.
    However, when i run the app, the server throws an exception at the accept call "could not find any key store entries to support the enabled cipher suites"
    Are any changes needed to the client code as well?

    ReplyDelete
    Replies
    1. client code? I didn't make any changes to the server code. I already had a self signed ssl certificate. Once you have those you don't really need to do much with those. Other then plug it in to what ever server setup you're using. Mine personally was a Node.js application.

      I used something like present in this: http://www.akadia.com/services/ssh_test_certificate.html through step 4.

      Since I had already generated my self signed ssl certs.

      Then used step 4 on this post to generate the keystore.

      While the info here is useful. It could have been structured better. For sure would have helped to have some of the commands explained.

      Delete
    2. https://gist.github.com/AaronOgle/5527063

      Here's my server communication file, with the ssl stuff working. As well as showing it used.

      Hope that helps.

      Delete
    3. Hi,
      Thanks for responding.
      Could you share more details on the process of generating self signed certificates?
      Like i said before, i get an exception "could not find trusted keystore for enabled cipher suites" when the server invokes the accept system call.

      Thanks

      Delete
    4. Like I said I generated it using the method described here: http://www.akadia.com/services/ssh_test_certificate.html

      or

      here: http://blog.didierstevens.com/2008/12/30/howto-make-your-own-cert-with-openssl/

      openssl genrsa -out host.key 4096

      openssl req -new -x509 -days 1826 -key host.key -out host.crt

      fill out the info it asks for.

      Then follow the steps here starting with step 4.

      keytool -import -alias ssltestcert -file host.crt
      -keypass ssltestcert -keystore C:\androidproject\res\raw\keystore
      -storetype BKS -storepass somepassword
      -providerClass org.bouncycastle.jce.provider.BouncyCastleProvider
      -providerpath c:\androidproject\libs\bcprov-jdk16-145.jar

      Might take some tweaking. But such is the nature of programming.

      Good luck

      Delete
    5. Thanks for the reply Aaron. :-)

      Delete