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(); } } |
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 |
/ 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: Android, Certificate, HTTPS, SSL
This entry was posted
on Saturday, July 21st, 2012 at 9:03 am and is filed under Android.
You can follow any responses to this entry through the RSS 2.0 feed.
You can leave a response, or trackback from your own site.
This is a horrible thing that should never be done! The problem is you are essentially breaking SSL and leaving yourself wide open to MitM attacks when you forget to remove this code prior to deploying to production. Don’t think you’ll forget? Microsoft, Google, Apple, Amazon, Yahoo, etc have all been found to have this problem, so unless you think your code release processes are better than theirs, you should spend a few second learning how to import a certificate onto your device/emulator instead. That way, it’s only trusted locally and you don’t run the risk of forgetting.
Thank you so much for the code, appreciate it!
Thank you!
Thank you, thank you, thank you.
I could KISS you… except I won’t. You’ve saved me with this code!