Android HTTPS and Not trusted server certificate Error

When working on a Android REST based web-service client on Android platform, I was getting following error when calling the API over the HTTPS:
javax.net.ssl.SSLException: Not trusted server certificate

The reason for this problem is quite obvious and that’s, on the development server, I was using the self signed certificate and it was causing this exception as self generated certificate is not a trusted certificate according to SSL rules and policies.

In fact, I had the similar problems while working in the .Net and PHP, and it was very simple to disable this. In PHP to ignore self signed certificate you just need a single line of code (see example here), and in .Net you just need to register a custom even handler for the verification (see example here).

However things are not that simple in Java and Android to disable/ignore the self signed certificates validation. There are many threads/posts on the web which are discussing similar problem but either these are for HttpsURLConnection (I was using HtpClient) or there were few exceptions or problems in sample code provided (probably due to different Android versions).

Anyway, after spending hours trying different solutions, I was make it able to work. So, I’m sharing this with you (and for myself as future reference). The first thing you need to do is to extend the SSLSocketFactory class to handle the certificate errors with custom rules. Here is the code for this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.*;
import java.security.cert.*;
import javax.net.ssl.*;
 
/**
 * Simple SSLFactory implementation which is designed to ignore all the SSL certificate.
 * Note: Please only use this for development testing (for self signed in certificate). Adding this to the
 * public application is a serious blunder.
 * @author Syed Ghulam Akbar
 */
public class SimpleSSLSocketFactory extends org.apache.http.conn.ssl.SSLSocketFactory
{
	private SSLSocketFactory sslFactory = HttpsURLConnection.getDefaultSSLSocketFactory ();
 
	public SimpleSSLSocketFactory (KeyStore truststore)
	throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
	{
		super(null);
 
		try
		{
			SSLContext context = SSLContext.getInstance ("TLS");
 
			// Create a trust manager that does not validate certificate chains and simply
			// accept all type of certificates
	                TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
	                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
	                        return new java.security.cert.X509Certificate[] {};
	                }
 
	                public void checkClientTrusted(X509Certificate[] chain, String authType)
					throws CertificateException {
	                }
 
	                public void checkServerTrusted(X509Certificate[] chain, String authType)
					throws CertificateException {
	                }
	        }};
 
	        // Initialize the socket factory
		context.init (null, trustAllCerts, new SecureRandom ());
		sslFactory = context.getSocketFactory ();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
 
	 @Override
	public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
                                  throws IOException, UnknownHostException
        {
	    return sslFactory.createSocket(socket, host, port, autoClose);
	}
 
	@Override
	public Socket createSocket() throws IOException {
	    return sslFactory.createSocket();
	}
}

The key code in this class is checkClientTrusted and checkServerTrusted methods for the new TrustManager, which simply ignores all kind of certificate validation errors (as we are not doing any verification in theses overrided methods). This is some what similar to the way you handle the RemoteCertificateValidationCallback event in .Net, but it looks a bit more complex.

This is still not end of the story, and you need more code. Next you need to create the ClientConnectionManager which uses the above created SimpleSSLSocketFactory class for all the new SSL connections. Here is the code for that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/ Setup a custom SSL Factory object which simply ignore the certificates
// validation and accept all type of self signed certificates
SSLSocketFactory sslFactory = new SimpleSSLSocketFactory(null);
sslFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
 
// Enable HTTP parameters
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
 
// Register the HTTP and HTTPS Protocols. For HTTPS, register our custom SSL Factory object.
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sslFactory, 443));
 
// Create a new connection manager using the newly created registry and then create a new HTTP client
// using this connection manager
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
client = new DefaultHttpClient(ccm, params);
 
// To-do: Get or Post the data using this newly created http client object

The credit for all this research goes to various authors and bloggers. My salute to all these community members).

Just a reminder (similar to that of SSL Certificate Validation Error in .Net post): Please remember to do this only in the development mode. Ignoring SSL certificate error in the production application is a serious blunder and this might put you in serious trouble when not carefully used. So, know what you are doing, and implement this at your own risk.

Please note that if you are having some certificate issues in production application, then it might be logical to have user import the correct certificate in the Android phone/tablet from the SD Card. The detail of this process goes here:

Tags: , , ,