SSL on Android: The Basics

(In thanks to The Guardian Project for some assistance in assembling this material, I am blogging a portion of The Busy Coder’s Guide to Android Development Version 4.6 that covers SSL on Android. This is the first part of a four-part series. The other parts include:

Note that if you are reading this in late 2013 or beyond, the material in the book may be newer than these blog posts.)


The traditional approach to securing HTTP operations is by means of SSL. Android supports SSL, much as ordinary Java does. Most of the time, you can just allow Android to do its thing with respect to SSL, and you will be fine. However, there may be times when you have to play a more direct role in SSL communications, to handle arbitrary SSL-encrypted endpoints, or to help ensure that your app is not the victim of a man-in-the-middle attack.

Basic SSL Operation

Generally speaking, SSL “just works”, for ordinary sites with ordinary certificates.

If you use an https: URL with HttpUrlConnection, HttpClient, or WebView, SSL handshaking will happen automatically, and assuming the certificates check out OK, you will get your result, just as if you had requested an http: URL.

However, DownloadManager only recently added support for SSL. Originally, requesting a download via DownloadManager with an https: scheme would result in java.lang.IllegalArgumentException: Can only download HTTP URIs. As of Android 4.0, SSL is supported. Hence, you need to be careful about making SSL requests via DownloadManager to ensure that you are only doing that on a relatively recent version of Android.

Certificate Verification

The first challenge comes in verifying the SSL certificate.

You can roughly divide SSL certificates into three types:

  • Those issued by a certificate authority (CA) that is recognized by Android (e.g., VeriSign) or was issued by a downstream CA whose upstream CA is one recognized by Android

  • Those issued by a CA that is not recognized by Android

  • Self-signed certificates, whether used temporarily (e.g., during development) or in production

Android can only transparently handle the first set, where the root CA for the certificate is one recognized by Android. And, for better and for worse, the roster of CAs recognized by Android varies between OS versions, as Google updates the OS cacerts roster.

If you encounter an SSL certificate that cannot be verified by Android, you will get a javax.net.ssl.SSLException: Not trusted server certificate exception from HttpUrlConnection and HttpClient, and you will need to decide for yourself how to handle that.

Custom TrustManager

The right solution is to build your own TrustManager that implements your business policies.

For example, if you want to validate a self-signed SSL certificate, you can implement a TrustManager that does so, by having a custom TrustStore. A TrustStore is a set of certificates (from a CA or self-signed) that a TrustManager can validate against. Nikolay Elenkov has an excellent writeup and sample code of implementing such a TrustStore. He also demonstrates how to have a composite TrustManager, one that uses the system’s TrustManager and your own (e.g., configured with your custom TrustStore), so certificates that are validated by either TrustManager are considered to be valid.

If you are trying to use this technique to validate certificates from a CA that is not recognized by Android, you may need to use Mr. Elenkov’s technique with multiple certificates, representing the upstream chain to the root CA.

Wildcard Certificates

Some certificates are difficult to validate, because they use wildcards.

For example, Amazon S3 is a file storage and serving “cloud” solution from Amazon.com. They allow you to define “buckets” containing “objects”, where each object then has its own URL. That URL is based on the name of the bucket and the name of the object. One option is for you to have the domain name of the URL be based on the name of the bucket, leaving the path to be solely the name of the object. This works, even with SSL, but Amazon needed to use a “wildcard SSL certificate”, one that matches *.s3.amazonaws.com, not just a single domain name. By default, this will fail on Android, as Android’s stock TrustManager will not validate wildcards for multiple domain name segments (e.g., http://misc.commonsware.com.s3.amazonaws.com/foo.txt). You will get an exception akin to:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException:
No subject alternative DNS name matching misc.commonsware.com.s3.amazonaws.com found

However, you could write a WildcardTrustManager or some such that relies on the system TrustManager to validate the rest of the certificate, while you validate the domain name matches the expectated value. The OpenDJ project has a series of available TrustManager implementations, including one that supports wildcards.

Anti-Pattern: Disabling SSL Certificate Validation

You will find various blog posts, StackOverflow answers, and the like that suggest that you simply disable SSL certificate validation, by implementing an “accept-all” TrustManager. Such a TrustManager basically implements the interface with empty stubs for methods like checkServerTrusted(), not throwing any exceptions.

Technically, this works. And, if you are using this only early on in development and if you swear upon a stack of $RELIGIOUS_TEXTS that you will replace this hack by the time you go to production, it is difficult to complain about this technique.

However, in production, ignoring SSL certificate validation errors opens your app up to man-in-the-middle attacks… which we will examine tomorrow.