Android WebView with https loadUrl shows blank/empty page
September 28, 2010
I recently encountered this problem while trying to develop a WebView that is supported on Doughnut (1.6) and above. When presented with a https url the WebView just renders a blank page.
It seems that until Froyo, Android didn’t provide a public API to let you manually decide if you wanted to proceed to an untrusted web site via a WebView.
Note that in this case it is not even that the web site was untrusted (in the conventional sense) – it is because Thawte is not in the default list of trusted certificate authorities on Android. If you use the standard web browser on Android, the browser presents a typical warning dialog (as presented below) that enables you to accept the certificate and carry on.
If you are using Froyo as the target SDK then you can use:
1 | engine = (WebView) findViewById(R.id.my_webview); |
2 | engine.setWebViewClient( new WebViewClient() { |
3 | public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { |
4 | handler.proceed() ; |
5 | } |
6 | } |
However, if you are not using Froyo then it seems you are out of luck. After some research, I concluded that I had the following options:
a) intercept all page requests and forward to external web browser if url begins with https (not a clean or nice user experience and totally unnecessary for Froyo and above)
b) add certificate of website to local keystore (in this case I am serving multiple web pages and the origin of many of these is not known until runtime)
c) make Froyo the minSDK and discard previous versions of Android (not a suitable option)
d) hack and use some private apis (the option described below)
To solve this problem we have to use a private interface (not published on SDK but present in real SDK runtime). As you can see in the Android src tree, theonReceivedSslError is indeed present (and used – it simply cancels the request) in Doughnut. However, this method is not presented to us in the SDK - it is hidden because it contains a parameter type SslError which is located in a hidden package. Therefore, we need to copy these src files into our project so that we can access them:
1) Copy WebViewClient.java into the package “android.webkit” within your src folder.
2) Copy SslError.java into the package “android.net.http” within your src folder.
3) Since we replicated the paths to the src files in the SDK, our code to override onSslError above now works on Android 1.6.
Caution: bear in mind that we are using a private API and Google reserve the right to change private APIs at any time – though in this case it is unlikely since they’ve now made this available in Froyo.
September 29, 2010 at 9:34 am
Hi, I have used this method and other method (like thishttp://wongcr.blogspot.com/2009/03/disabling-certificate-validation-in.html) and I can’t see a https page in the WebView. Can you help me?
September 29, 2010 at 9:42 am
I can try. Your request for help is a bit vague though…
September 29, 2010 at 10:26 pm
Sorry, I did’t know if I should write a large post. My problem is with the HttpPost connections, I create an “DefaultHttpClient” and a “HttpPost”, next I create a list of values to send and finally execute it.
This work in http conections, but in https conections I try the methods I find on Internet and don’t get it to work (Get not trusted certificate).
The last code (with custom SSLShocketFactory and TrustManager):
try {
// //
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme(“https”,new EasySSLSocketFactory(), 443));
HttpParams params = new BasicHttpParams();
SingleClientConnManager mgr = new SingleClientConnManager(params, schemeRegistry);
DefaultHttpClient hc = new DefaultHttpClient(mgr, params);
// /
// DefaultHttpClient hc = new DefaultHttpClient();
ResponseHandler res = new BasicResponseHandler();
HttpPost postMethod = new HttpPost(DIRECCION);
List nameValuePairs = new ArrayList(3);
nameValuePairs.add(new BasicNameValuePair(“usuario”, nom));
nameValuePairs.add(new BasicNameValuePair(“tipo_firma”, tip));
nameValuePairs.add(new BasicNameValuePair(“string_img”, img));
postMethod.setEntity(new UrlEncodedFormEntity(nameValuePairs));
String response = hc.execute(postMethod, res);
return response;
} catch (ClientProtocolException e) {
pasos = “Excepción: ” + e.toString();
} catch (IOException e) {
// “Error 1: Unable to establish communication with the server. Check your configuration.”
pasos = pasos + “Excepción: ” + e.toString();
}
if you can help me, it would very helpfull.
PD: Sorry for my bad english…
September 30, 2010 at 7:33 am
The best way would be to add the server’s public certificate to your Android App’s trusted store:
http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html
Otherwise you could simply stop checking all hosts for their certificate:
http://stackoverflow.com/questions/995514/https-connection-android/1000205#1000205
September 30, 2010 at 9:39 am
Finally, your post give me an idea. I change the DefaultHttpClient for HttpsURLConection, using OutputStream and InputStream to the comunication. Next I apply the second link you give me (I visited it yet but didn’t help me in my previously idea).
I can’t use the SSL certificate creator because I haven’t direct access to the server of the proyect.
the code of Send:
try {
// //
String location = “usuario=” + nom + “&tipo_firma=” + tip + “&string_img=” + img;
URL url = new URL(DIRECCION);
trustAllHosts();
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
https.setHostnameVerifier(DO_NOT_VERIFY);
https.setRequestMethod(“POST”);
https.setDoOutput(true);
https.setUseCaches(false);
https.setRequestProperty(“Content-Type”,
“application/x-www-form-urlencoded”);
https.connect();
OutputStream ost = https.getOutputStream();
DataOutputStream out = new DataOutputStream(ost);
out.writeBytes(location);
out.flush();
ost = null;
InputStream ist = https.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(ist));
StringBuffer sb = new StringBuffer();
char[] c = new char[1];
while (in.read(c, 0, 1) == 1) {
sb.append(c[0]);
}
String response = sb.toString();
// /
return response;
} catch (ClientProtocolException e) {
pasos = “Excepción: ” + e.toString();
} catch (IOException e) {
// “Error 1: Unable to establish communication with the server. Check your configuration.”
pasos = pasos + “Excepción: ” + e.toString();
}
Next adding the trust part:
// always verify the host – dont check for certificate
final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
/**
* Trust every server – dont check for any certificate
*/
private static void trustAllHosts() {
// Create a trust manager that does not validate certificate chains
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 {}
} };
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance(“TLS”);
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
Thanks for your help.
October 4, 2010 at 11:25 am
You’re welcome
January 12, 2012 at 9:41 pm
Crazy Bob’s article is a solution for SSL issues with the HttpClient, not with a WebView. I don’t believe you can add a certificate to the app’s trusted store and have that used by a WebView.
October 12, 2010 at 3:31 am
Hi,
Just wanted to thank you to have resolved my problem in 2 seconds.
Working on eclair 2.1 at the moment I had exactly the same issue you described. Your fixed avoided me a lot of troubles.
thanks
William
December 27, 2010 at 4:55 am
Thanks a lot, the above solution works.
February 23, 2011 at 2:38 pm
Thank you very much, this is excellent!!!
I had been struggling with this for some hours before I found your solution.
June 28, 2011 at 10:31 pm
Thanks, was having the same problem in a project targeting Android 1.6. Now I know what the heck is going on.
July 1, 2011 at 9:53 pm
[...] WebView just swallowed this with no error messages, but a bit of googling and found the following blog post about how to work round the certificate miss match and even how to make it work with Android [...]
July 14, 2011 at 2:05 pm
Hi All,
I am trying to retrieve POST parameter from Response. but not able get it from android Webview.
Could anyone help me or give an idea to retrieve POST data from response URL.
Thanks & Regards
July 14, 2011 at 7:05 pm
I don’t think webview is very conducive for that kind of thing but the link below might help.
http://stackoverflow.com/questions/3134389/access-the-http-response-headers-in-a-webview
August 15, 2011 at 9:57 am
I have applied your trick and it works fine until I run on a real Android device 1.6 or 2.1. Then the overloading of classes is stop by the DEX loader !!! any tricks to get around ?
August 15, 2011 at 10:05 am
08-15 12:03:41.938: DEBUG/dalvikvm(3003): DexOpt: ‘Landroid/net/http/SslError;’ has an earlier definition; blocking out
08-15 12:03:41.938: DEBUG/dalvikvm(3003): DexOpt: ‘Landroid/webkit/WebViewClient;’ has an earlier definition; blocking out
08-15 12:03:41.973: DEBUG/dalvikvm(3003): DexOpt: not verifying ‘Landroid/net/http/SslError;’: multiple definitions
08-15 12:03:41.973: DEBUG/dalvikvm(3003): DexOpt: not verifying ‘Landroid/webkit/WebViewClient;’: multiple definitions
08-15 12:03:42.153: INFO/dalvikvm(3003): DexOpt: not resolving ambiguous class ‘Landroid/webkit/WebViewClient;’
August 26, 2011 at 9:10 pm
Hi Damian,
I’ve been struggling a couple of nights getting postUrl to work when posting to a https site. I tried to use all sorts of TrustedManagers etc etc but with no luck.
Then I found this gem of yours, read it and in less than 10 minutes I was up’n running.
I bow my head in humble submission.
Thank you.
/brian
January 11, 2012 at 12:28 am
Hello, Damian thnx for the tutorial but i still dont manage to get it work. mayb if u have some time u can help me load https in my webview
package here.testing.a.cool.app;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class WebPageLoader extends Activity
{
final Activity activity = this;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.getWindow().requestFeature(Window.FEATURE_PROGRESS);
setContentView(R.layout.main);
WebView webView = (WebView) findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress)
{
activity.setTitle(“Grabbing the bits and the bytes..”);
activity.setProgress(progress * 100);
if(progress == 100)
activity.setTitle(R.string.app_name);
}
});
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
{
// Handle the error
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
view.loadUrl(url);
return true;
}
});
webView.loadUrl(“https://website.willnot.open”);
}
}
January 17, 2012 at 1:40 pm
Thanks a lot!!!!
April 18, 2012 at 5:31 pm
Great post Damian.
As always your articles are most useful.
Thank you!