Wednesday, July 28, 2010

Android - Mutual Authentication Using WebView

Hi all,
I am back with new and a great challenge in the android application.
Using the WebView and send the client certificate.
Confused???

Now start searching over the internet and go line by line of the API documentation of the WebView but after spending 2-3-4...8 days where are we... No solution on the internet.

Now start doing reverse engineering. Go to the source of android WebView and finally at a corner you will find that HTTPS connection is being used by Android OS in low level.

Again after a long search you will find that android.net.http.HttpsConnection class is being used by WebView for all the https connection. When you start looking the code of this class you will find that it always sends the hard coded value as null for client certificate in static variable "mSslSocketFactory". So we are again stuck to solve this problem.

Here comes the Java Reflection which can be used to solve this problem. If android has not revealed any API to set this variable then use reflection to set your client certificate which will be recognized by server.
Below source tells how can it be achieved.

Initialize WebView and before calling the

webview.loadUrl("https://www.myserver.com");
call the below method code.

private void setClientCertificateConnectionPicker(InputStream input, String password) {

try {
KeyManagerFactory keyManagerFactory = KeyManagerFactory
.getInstance("X509");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(input, password);
keyManagerFactory.init(keyStore, "changeit".toCharArray());
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {

public java.security.cert.X509Certificate[] getAcceptedIssuers() {
Log.d(TAG, "getAcceptedIssuers--Checking issuers");
return null;
}

public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
Log.d(TAG, "checkClientTrusted--Checking client authentication");
}

public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
//Validate your server here
for (int i = 0; i < certs.length; i++) {
Log.d(TAG, "\t" + certs[i].getIssuerX500Principal().getName());
Log.d(TAG, "\t" + certs[i].getIssuerDN().getName());
}
}
}
};

SSLContext sc = SSLContext.getInstance("TLS");
sc.init(keyManagerFactory.getKeyManagers(), trustAllCerts, new java.security.SecureRandom());
// BELOW STATMENTS USES JAVA REFLECTION TO SET THE CLIENT
// CERTIFICATE AS
// SSL SOCKET FACTORY.

Class c = Class.forName("android.net.http.HttpsConnection");
Field[] fieldlist = c.getDeclaredFields();
for (int i = 0; i < fieldlist.length; i++) {
Field fld = fieldlist[i];
if (fld.getName().equals("mSslSocketFactory")) {
fld.setAccessible(true);
fld.set(null, sc.getSocketFactory());
}
}
} catch (ClassNotFoundException e) {
Log.e(TAG, "--setClientCertificateConnectionPicker-- Class not found", e);
} catch (IllegalArgumentException e) {
Log.e(TAG, "--setClientCertificateConnectionPicker-- IllegalArgumentException", e);
} catch (IllegalAccessException e) {
Log.e(TAG, "--setClientCertificateConnectionPicker-- IllegalAccessException", e);
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "--setClientCertificateConnectionPicker-- NoSuchAlgorithmException", e);
} catch (KeyStoreException e) {
Log.e(TAG, "--setClientCertificateConnectionPicker-- KeyStoreException", e);
} catch (CertificateException e) {
Log.e(TAG, "--setClientCertificateConnectionPicker-- CertificateException", e);
} catch (IOException e) {
Log.e(TAG, "--setClientCertificateConnectionPicker-- IOException", e);
} catch (UnrecoverableKeyException e) {
Log.e(TAG, "--setClientCertificateConnectionPicker-- UnrecoverableKeyException", e);
} catch (KeyManagementException e) {
Log.e(TAG, "--setClientCertificateConnectionPicker-- KeyManagementException", e);
}

}




And here you go.

Write me for any issue in this.

Three Cheers to Authentication!!!

6 comments:

  1. Great!

    I had the same problem.
    This solution solves that!
    Thank you!

    ReplyDelete
  2. nice one!

    http://code.google.com/p/sandrob/

    ReplyDelete
  3. I know its already older post but just from curiosity did you manage same thing on newer OS. ICS ?

    ReplyDelete
  4. @Vjekoslav
    I didn't try this on ICS but not sure it should work on that too.
    Did you try and failed?

    ReplyDelete
  5. greeeeeeaaaaatttt! I was finding how to load urls in jellybean! now I found this !!!!

    ReplyDelete
  6. Trying to get this to work on android 4.4.2 and keep getting:

    'Client certificate request from rejected. (Client certificates not supported in WebView)

    ReplyDelete