Showing posts with label SSL. Show all posts
Showing posts with label SSL. Show all posts

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.

Programmatically Disabling Java SSL Certificate Check for Testing

Continuous integration testing with Java SSL code is prone to certificate mismatch problems. Sometimes valuable development time can be saved by disabling just the certificate verification logic in the SSL client while preserving all other security logics. This technique is particularly useful for testing with a self-signed certificate because it eliminates the need to install the certificate on every client machine or device that needs to communicate with the SSL server. It is perfectly safe in a well-controlled development environment.

First, we create stub implementations of HostnameVerifier and X509TrustManager.
public static class NullVerifier implements HostnameVerifier {
      @Override
      public final boolean verify(final String hostname,
                                  final SSLSession sslSession) {
         return true;
      }

   }
   
   public static class NullTrustManager implements X509TrustManager {
      @Override
      public void checkClientTrusted(final X509Certificate[] chain, 
                                     final String authType) 
         throws CertificateException {
      }

      @Override
      public void checkServerTrusted(final X509Certificate[] chain, 
                                     final String authType) 
         throws CertificateException {
      }

      @Override
      public final X509Certificate[] getAcceptedIssuers() {
         return new X509Certificate[] {};
      }

Then, we install our stub implementations to the SSL connection.
      // Configure SSL Context
      SSLContext sslContext = SSLContext.getInstance("TLS");
      X509TrustManager nullTrustManager = new NullTrustManager();
      TrustManager[] nullTrustManagers = {nullTrustManager};
      sslContext.init(null, nullTrustManagers, new SecureRandom());

      // Create HTTPS connection
      URL url = new URL("https", "127.0.0.1", 8443, "/ssltest");
      HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
      conn.setHostnameVerifier(new NullVerifier());
      conn.setSSLSocketFactory(sslContext.getSocketFactory());

Configure Jetty Maven Plugin for SSL

Documentation on Jetty Maven Plugin from Eclipse Foundation is not as complete as its predecessor from Codehaus. Here is a sample pom.xml for configuring the plugin for SSL. The highlighted section is the configuration of a SSL connector.
<build>
  <plugins>
    <plugin>
      <groupid>org.mortbay.jetty</groupid>
      <artifactid>jetty-maven-plugin</artifactid>
      <version>7.0.2.v20100331</version>
      <configuration>
        <connectors>
          <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
           <port>8080</port>
           <maxidletime>60000</maxidletime>
          </connector>
          <connector implementation="org.eclipse.jetty.server.ssl.SslSelectChannelConnector">
           <port>8443</port>
           <maxidletime>60000</maxidletime>
           <keystore>${basedir}/ssl/server.keystore</keystore>
           <password>sample</password>
           <keypassword>sample</keypassword>
          </connector>
         </connectors>
      </configuration>
     </plugin>
   </plugins>
</build>