WebView HTTPS workaround. How to?

yappa

Member
Licensed User
Longtime User
Hello!
As you may know WebView on 2.1\3 have an issue with SSL (some https pages may wont load due to certificate problem).

I am stuck with this problem :(
Some page that i need for oauth2 use https and works ok on my ISC phone, and don't work on older platforms.

This problem is well-known and people suggest to override "onReceivedSslError" event. Like here: Android WebView with https loadUrl shows blank/empty page « Damian Flannery's Blog

How i can do it with B4A? I think it can be done with an lib, or mayb reflector magic, but i am totally noob in pure java, and dont know how to start.

Thanks.
 

yappa

Member
Licensed User
Longtime User
You can download the html with Http library (see HttpUtils2) and then load it into a WebView.

Can't because of the same problem - HTTPUtils2 returns failed job:
B4X:
Error: javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
 
Upvote 0

yappa

Member
Licensed User
Longtime User
Change HttpUtils2Service - Service_Create and use hc.InitializeAcceptAll.

Yes, its work. But its kinda problematic - the "implicit flow" authorization of needed website has many steps, and ui without css and images looks gross - users never gonna believe that this is the "original" site, and probably will not auth :)
 
Upvote 0

yappa

Member
Licensed User
Longtime User
Ok, case closed - i make my first lib and used the method described in first post. That was not so scary :)

Thanks!!
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
The fix that you linked to in your first post involves updating the WebViewClient that is attached to the WebView.
The WebViewClient is created when the WebView is created, it's not possible to get the already created WebViewClient and add the required onReceivedSslError() method to it.

I did recompile the B4A WebView a few months ago to enable clipboard selection of text within a WebView.
So i took that code, removed the clipboard functionality (so it's now a barebones decompiled B4A WebView) and updated the WebViewClient - adding the required:

B4X:
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
   handler.proceed();
}

Recompiled and named SslErrorWebView, it should behave exactly the same as the default B4A WebView but with the updated WebViewClient hopefully solve your problem.
(Only with Android versions Froyo and later though - the fix won't work for older than Froyo devices it seems).

So if you want to give it a test it's attached, you can only create an SslErrorWebView in code, you won't be able to use the Designer to add a SslErrorWebView to your project.

Martin.
 

Attachments

  • SslErrorWebView.zip
    5.3 KB · Views: 611
Upvote 0

yappa

Member
Licensed User
Longtime User
So if you want to give it a test it's attached, you can only create an SslErrorWebView in code, you won't be able to use the Designer to add a SslErrorWebView to your project.

Martin.

Oh thanks alot! Your lib is working and dont kills events (Pageload etc) of webview like my one :)
I am just started to make my own callbacks but now i will just use your lib - thanks man, u saved my time!
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
Did you start to write your own WebViewClient library and then add that to a WebView replacing the default B4A WebViewClient with your own WebViewClient?

That's a better idea than decompiling and recompiling the B4A WebView as you can still use the Designer to add a WebView to your project.
And if the B4A WebView gets updated in future versions of B4A you'd just have to update your WebViewClient instead of decompiling and recompiling the new B4A WebView.

How about this as an example:

B4X:
package uk.co.martinpearman.b4a.webviewextras;

import android.net.http.SslError;
import android.webkit.HttpAuthHandler;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.ActivityObject;
import anywheresoftware.b4a.BA.Author;
import anywheresoftware.b4a.BA.Events;
import anywheresoftware.b4a.BA.Hide;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;

@ActivityObject
@Author("Martin Pearman")
@Events(values = { "OverrideUrl (Url As String) As Boolean", "PageFinished (Url As String)", "UserAndPasswordRequired (Host As String, Realm As String) As String()" })
@ShortName("MyWebViewClient")
@Version(1.00F)
public class MyWebViewClient extends WebViewClient {
   private BA mBA;
   private String mEventName;

   public void Initialize(BA pBA, WebView WebView1, String EventName) {
      mBA = pBA;
      mEventName = EventName.toLowerCase(BA.cul);
      WebView1.setWebViewClient(this);
   }

   @Hide
   public void onPageFinished(WebView view, String url) {
      mBA.raiseEvent(MyWebViewClient.this, mEventName + "_pagefinished", new Object[] { url });
   }

   @Hide
   public boolean shouldOverrideUrlLoading(WebView view, String url) {
      Boolean b = (Boolean) mBA.raiseEvent(MyWebViewClient.this, mEventName + "_overrideurl", new Object[] { url });
      if (b != null)
         return b.booleanValue();
      return false;
   }

   @Hide
   public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
      Object o = mBA.raiseEvent(MyWebViewClient.this, mEventName + "_userandpasswordrequired", new Object[] { host, realm });
      if (o == null) {
         handler.cancel();
      } else {
         String[] s = (String[]) o;
         handler.proceed(s[0], s[1]);
      }
   }

   @Hide
   public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
      handler.proceed();
   }
}

In B4A you can now create the WebView using the Designer or in code, and to replace the B4A WebViewClient you'd just do something like:

B4X:
Dim MyWebViewClient1 As MyWebViewClient
MyWebViewClient1.Initialize(MyWebView, "MyEventName")

Martin.
 

Attachments

  • MyWebViewClient.zip
    3.6 KB · Views: 520
Upvote 0

dbalman

Member
Licensed User
Longtime User
Did you start to write your own WebViewClient library and then add that to a WebView replacing the default B4A WebViewClient with your own WebViewClient?

That's a better idea than decompiling and recompiling the B4A WebView as you can still use the Designer to add a WebView to your project.
And if the B4A WebView gets updated in future versions of B4A you'd just have to update your WebViewClient instead of decompiling and recompiling the new B4A WebView.

How about this as an example:

B4X:
package uk.co.martinpearman.b4a.webviewextras;

import android.net.http.SslError;
import android.webkit.HttpAuthHandler;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.ActivityObject;
import anywheresoftware.b4a.BA.Author;
import anywheresoftware.b4a.BA.Events;
import anywheresoftware.b4a.BA.Hide;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;

@ActivityObject
@Author("Martin Pearman")
@Events(values = { "OverrideUrl (Url As String) As Boolean", "PageFinished (Url As String)", "UserAndPasswordRequired (Host As String, Realm As String) As String()" })
@ShortName("MyWebViewClient")
@Version(1.00F)
public class MyWebViewClient extends WebViewClient {
   private BA mBA;
   private String mEventName;

   public void Initialize(BA pBA, WebView WebView1, String EventName) {
      mBA = pBA;
      mEventName = EventName.toLowerCase(BA.cul);
      WebView1.setWebViewClient(this);
   }

   @Hide
   public void onPageFinished(WebView view, String url) {
      mBA.raiseEvent(MyWebViewClient.this, mEventName + "_pagefinished", new Object[] { url });
   }

   @Hide
   public boolean shouldOverrideUrlLoading(WebView view, String url) {
      Boolean b = (Boolean) mBA.raiseEvent(MyWebViewClient.this, mEventName + "_overrideurl", new Object[] { url });
      if (b != null)
         return b.booleanValue();
      return false;
   }

   @Hide
   public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
      Object o = mBA.raiseEvent(MyWebViewClient.this, mEventName + "_userandpasswordrequired", new Object[] { host, realm });
      if (o == null) {
         handler.cancel();
      } else {
         String[] s = (String[]) o;
         handler.proceed(s[0], s[1]);
      }
   }

   @Hide
   public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
      handler.proceed();
   }
}

In B4A you can now create the WebView using the Designer or in code, and to replace the B4A WebViewClient you'd just do something like:

B4X:
Dim MyWebViewClient1 As MyWebViewClient
MyWebViewClient1.Initialize(MyWebView, "MyEventName")

Martin.

Hi Martin,

I am having the same issue as the OP in that I'm trying to replace the he default B4A WebViewClient with your WebViewClient attached to this post to resolve certificate errors. I have a WebView added in the designer, however, I am unable to figure out how to replace it with your MyWebViewClient library using the code you provided:

B4X:
Dim MyWebViewClient1 As MyWebViewClient
MyWebViewClient1.Initialize(MyWebView, "MyEventName")

I have the name of my program's WebView replacing MyWebView in the code above, but what is the event I should be listening for in "MyEventName" to make it work?

BTW, I am a HUGE fan of yours Martin. This is my first post, but I have been lurking for a while and your patience and assistance with other newbies like myself is quite remarkable and very much appreciated.

Thanks,

Dan
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
Hi Dan.

That early attempt to handle the WebViewClient onReceivedSslError did not raise any event.
It blindly instructed the WebView to proceed regardless of the cause of the error:

B4X:
  public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
  handler.proceed();
  }

A while back i completely rewrote my WebViewExtras library, so far i've not had time to fully test, document and upload the new version.

The new version of WebViewExtras has a method SetWebViewClient(WebViewClient1 As android.webkit.WebViewClient).
And it has a new object DefaultWebViewClient which raises an event ReceivedSslError(SslErrorHandler1 As SslErrorHandler, SslError1 As SslError).

The SslError object allows you to examine the error and the SslErrorHandler object allows you to Proceed or Cancel loading the page that raised the event.

Have a look at this example:

B4X:
Sub Process_Globals

End Sub

Sub Globals
   Dim WebView1 As WebView
   Dim WebViewExtras1 As WebViewExtras
End Sub

Sub Activity_Create(FirstTime As Boolean)
   WebView1.Initialize("")
   Activity.AddView(WebView1, 0, 0, 100%x, 100%y)
   
   WebViewExtras1.Initialize(WebView1)
   '   WebViewExtras1 now has all the methods and properties of WebView1 plus it's additonal methods and properties
   '   so you can use WebView1 to get/set WebView properties/methods
   '   or use WebViewExtras1 to get/set WebView1 properties/methods with the additional properties/method of WebViewExtras
   
   Dim WebViewClient1 As DefaultWebViewClient
   WebViewClient1.Initialize("WebViewClient1")
   
   WebViewExtras1.SetWebViewClient(WebViewClient1)
   
   WebViewExtras1.LoadUrl("http://google.co.uk/")
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub WebViewClient1_ReceivedSslError(SslErrorHandler1 As SslErrorHandler, SslError1 As SslError)
   Log("WebViewClient1_ReceivedSslError: "&SslError1.GetUrl)
   
   '   you might want to take action depending on the type of the SSL error:
   Select SslError1.GetPrimaryError
     Case SslError1.SSL_DATE_INVALID
       Log("SSL_DATE_INVALID")
     Case SslError1.SSL_EXPIRED
       Log("SSL_EXPIRED")
     Case SslError1.SSL_IDMISMATCH
       Log("SSL_IDMISMATCH")
     Case SslError1.SSL_INVALID
       Log("SSL_INVALID")
     Case SslError1.SSL_MAX_ERROR
       Log("SSL_MAX_ERROR")
     Case SslError1.SSL_NOTYETVALID
       Log("SSL_NOTYETVALID")
     Case SslError1.SSL_UNTRUSTED
       Log("SSL_UNTRUSTED")
   End Select
   
   '   you might want to compare the url that raised the error with a known trusted URL and Proceed or Cancel with the page loading:
   If SslError1.GetUrl="http://my_trusted_domain.com" Then
     SslErrorHandler1.Proceed
   Else
     SslErrorHandler1.Cancel
   End If
   
End Sub

You now have full control over how to handle the SSL error and can continue to load the web page that raised the error or cancel it's loading.

You can get the current version of the new WebViewExtras from here: http://android.martinpearman.co.uk/b4a/temp/WebViewExtras_library_files_20130725.zip.
And the example project is attached to this post.

If you are already using the old version of WebViewExtras and need help updating to the new version then let me know and i'll try and help.

Martin.
 

Attachments

  • ReceivedSslError_example.zip
    6.3 KB · Views: 391
Upvote 0

Derek Johnson

Active Member
Licensed User
Longtime User
I'm just trying out the example and it correctly detects SSL errors. This URL gives SSL_UNTRUSTED for instance:

https://librarycat.portsmouth.gov.uk/

(It's because there is no proper root certificate at present - 30 Apr 2015)

I'm trying to use the addJavascriptInterface method but I can't get the calls to work.

B4X:
Sub Process_Globals

End Sub

Sub Globals
    Dim WebView1 As WebView
    Dim WebViewExtras1 As WebViewExtras
    Dim WebViewClient1 As DefaultWebViewClient
End Sub

Sub Activity_Create(FirstTime As Boolean)
    WebView1.Initialize("")
    Activity.AddView(WebView1, 0, 0, 100%x, 100%y)
    WebViewExtras1.Initialize(WebView1)  
    WebViewClient1.Initialize("WebViewClient1")

    WebViewExtras1.SetWebViewClient(WebViewClient1) 

    WebViewExtras1.addJavascriptInterface(WebViewClient1, "B4A")
      'What should the parameter be?
  
    WebViewExtras1.LoadUrl("http://google.co.uk/")

    'WebViewExtras1.LoadUrl("https://librarycat.portsmouth.gov.uk") 'This has a faulty certificate
  
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub WebViewClient1_PageFinished (URL As String) As Boolean
    Log("=== Loaded URL " & URL)
    Dim task As String
    task=$"
     var dlength = document.body.innerHTML.length;
     B4A.CallSub('jTitle',true, document.title,dlength)
    "$
    WebViewExtras1.executeJavascript(task)
End Sub

Sub jTitle(title As String, doclength As String)
    Log("Title of Doc was " & title  & " Length was " & doclength)
End Sub

I'm getting the URL Loaded message but nothing after that.
The key issue is I think this statement:

WebViewExtras1.addJavascriptInterface(WebViewClient1, "B4A")

I've tried it with WebView1 as the parameter but that doesn't work either. It wants a JavaObject but how to create it.

Derek
 
Last edited:
Upvote 0

Derek Johnson

Active Member
Licensed User
Longtime User
I've just figured out the answer to this myself, when I found an earlier post

I needed to add the JavaObject library and then make this addition:

The key section is:

B4X:
    Dim JavascriptInterface1 As DefaultJavascriptInterface
    JavascriptInterface1.Initialize
    WebViewExtras1.AddJavascriptInterface(JavascriptInterface1, "B4A")
 
Upvote 0

amidgeha

Active Member
Licensed User
Longtime User
I've just figured out the answer to this myself, when I found an earlier post

I needed to add the JavaObject library and then make this addition:

The key section is:

B4X:
    Dim JavascriptInterface1 As DefaultJavascriptInterface
    JavascriptInterface1.Initialize
    WebViewExtras1.AddJavascriptInterface(JavascriptInterface1, "B4A")
Thanks a lot, you solved my 1-year old problem, I was not able to get response from javascript to B4A.
 
Upvote 0

oceanwanderlust

Member
Licensed User
Longtime User
It looks like Android/Google/WebView is doing some additional SSL verification in the past few weeks. Webpages in my app are suddenly silently failing to load because of Transparent Certificate and/or using SHA-1, but warwound's example and updated webviewextras allows me to bypass this error until I can update the backend server.

THANKYOU WARWOUND!!!
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
Hi Martin
I am unable to download the libraries from the above link.
Can you please look into it.

Thank you

Hi my website is offline as i've change my address and am waiting for broadband to be activated at my new address.
I've attached WebViewExtras2-v2.20.zip for you.

Hoping to be back up and running this coming Friday.
 
Upvote 0
Top