Thursday, 29 November 2012

How to bypass SSL certificate checking in Apache Cordova 2.2 on Android



How to bypass SSL certificate checking in Apache Cordova 2.2 on Android

(or how to override CordovaWebViewClient in versions of Cordova 1.9+)

I came across a strange issue where I was unable to access a secure URL using HTTPS.  At first I thought it was due to the Cordova whitelist since there was an issue with it in Cordova 2.1.  In order to access an external URL you have to whitelist them eg
<access origin="https://www.wikipedia.org" subdomains="true"/>
This didn't help though and even if I allowed all URLS using
<access origin = "*"/>
it still didn't work.  Strangely though it was only one secure URL I couldn't access, not all of them

As it turns out, it was probably to do with the way Cordova assesses valid certificates.  When an Android app is signed and deployed it is deployed in 'production mode' which means that any secure URL accessed must have a valid certificate signed by a certificate authority (CA), so cannot be self-signed.  Something about this particular certificate, though signed by a CA, was being rejected by Cordova, which led me to raise this bug: https://issues.apache.org/jira/browse/CB-1947

Since I'm impatient, I decided to try and bypass the certificate checking.  Searching led me to various solutions, all based on extending CordovaWebViewClient and overriding the onReceivedSslError() method. For example:

public class SSLAcceptingWebViewClient extends CordovaWebViewClient {
    public SSLAcceptingWebViewClient(DroidGap ctx) {
        super(ctx);
    }
    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
            handler.proceed();
    }
 
}

This was the easy part.  I then needed to tell Cordova to use my new class, rather than the default one.  This seemed simple as the advice I found on stackoverflow was to set it like this:
this.setWebViewClient(this.appView, new MyWebViewClient(this));
Unfortunately, this method signature no longer existed in Cordova 2.2.  Altering it to
this.appView.setWebViewClient( new SSLAcceptingWebViewClient(this));
compiled fine.  However, it resulted in a NullPointerException in the onPageLoad() method as seen by various others with no resolution.  I couldn't find any solution to this on the web, so I had to dig into the source code for Cordova.  Since it was a NullPointerException something had to be null, and the offending line was
if (!this.appView.useBrowserHistory) {
Since 'this' couldn't be null and useBrowserHistory is a boolean, it could only be appView that was null.  Some messing around finally led me to this solution in my main activity class

public class MyActivity extends DroidGap {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.init();
     
        CordovaWebViewClient webViewClient = new SSLAcceptingWebViewClient(this);
        webViewClient.setWebView(this.appView);
        this.appView.setWebViewClient(webViewClient);
     
        super.loadUrl("file:///android_asset/www/index.html");
    }
}

And it works! I've successfully bypassed the SSL certificate checking!  Obviously this is inherently insecure, so I can't recommend it as a permanent solution.  I'm hoping that the above bug will be resolved in the next version of Cordova and I can switch my bypass solutions off.

If you need to override CordovaWebViewClient, this seems to be the way to do it.  If you know a better way, please do let me know

No comments:

Post a Comment