Check in the onReceivedSslError() method of a WebViewClient if a certificate is signed from a specific self-signed CA

I would like to override the onReceivedSslError() of a WebViewClient. Here I want to check if the error.getCertificate() certificate is signed from a self-signed CA and, only in this case, call the handler.proceed(). In pseudo-code:

@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
    SslCertificate serverCertificate = error.getCertificate();

    if (/* signed from my self-signed CA */) {
        handler.proceed();
    }
    else {
        super.onReceivedSslError(view, handler, error);
    }
}

The public key of my CA is saved in a BouncyCastle resource called rootca.bks. How can I do?

  • SQL Android Query formulation with multiple conditions
  • YouTube video playback with ExoPlayer
  • Disable keep screen on
  • Questions every Smartphone(Iphone,Android) Developer should be able to answer?
  • Android Wikipedia Article search not display proper in my webview?
  • Go back to previous screen without creating new instance
  • Related posts:

    Android: Animation gets clipped by parent view
    aapt not found when building new Android app in NetBeans
    Are there reasons not to use lombok with android studio
    JSON jsonObject.optString() returns String “null”
    Remove class= attribute
    What is the best replacement for a gallery in Android?
  • Determine if a view is on screen - Android
  • Eclipse AVD Nexus 5 emulator not working
  • ImageView - draw translucent color on top for its focused state?
  • Starting Frame-By-Frame Animation
  • Set the background colour of a Layout view to a gradient in Android?
  • How to avoid session timeout in Android
  • 3 Solutions collect form web for “Check in the onReceivedSslError() method of a WebViewClient if a certificate is signed from a specific self-signed CA”

    I think you can try as the following:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        try {
            WebView webView = (WebView) findViewById(R.id.webView);
            if (webView != null) {
                // Get cert from raw resource...
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                InputStream caInput = getResources().openRawResource(R.raw.rootca); // stored at \app\src\main\res\raw
                final Certificate certificate = cf.generateCertificate(caInput);
                caInput.close();
    
                String url = "https://www.yourserver.com";
                webView.setWebViewClient(new WebViewClient() {                    
                    @Override
                    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                        // Get cert from SslError
                        SslCertificate sslCertificate = error.getCertificate();
                        Certificate cert = getX509Certificate(sslCertificate);
                        if (cert != null && certificate != null){
                            try {
                                // Reference: https://developer.android.com/reference/java/security/cert/Certificate.html#verify(java.security.PublicKey)
                                cert.verify(certificate.getPublicKey()); // Verify here...
                                handler.proceed();
                            } catch (CertificateException | NoSuchAlgorithmException | InvalidKeyException | NoSuchProviderException | SignatureException e) {
                                super.onReceivedSslError(view, handler, error);
                                e.printStackTrace();
                            }
                        } else {
                            super.onReceivedSslError(view, handler, error);
                        }
                    }
                });
    
                webView.loadUrl(url);
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
    // credits to @Heath Borders at http://stackoverflow.com/questions/20228800/how-do-i-validate-an-android-net-http-sslcertificate-with-an-x509trustmanager
    private Certificate getX509Certificate(SslCertificate sslCertificate){
        Bundle bundle = SslCertificate.saveState(sslCertificate);
        byte[] bytes = bundle.getByteArray("x509-certificate");
        if (bytes == null) {
            return null;
        } else {
            try {
                CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
                return certFactory.generateCertificate(new ByteArrayInputStream(bytes));
            } catch (CertificateException e) {
                return null;
            }
        }
    }
    

    If failed validation, logcat will have some information such as java.security.SignatureException: Signature was not verified...

    If success, here’s a screenshot:

    BNK's screenshot

    I think this should work (SSL_IDMISMATCH means “Hostname mismatch”).

    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        SslCertificate serverCertificate = error.getCertificate();
    
        if (error.hasError(SSL_UNTRUSTED)) {
            // Check if Cert-Domain equals the Uri-Domain
            String certDomain = serverCertificate.getIssuedTo().getCName();
            if(certDomain.equals(new URL(error.getUrl()).getHost())) {
              handler.proceed();
            }
        }
        else {
            super.onReceivedSslError(view, handler, error);
        }
    }
    

    If “hasError()” is not working, try error.getPrimaryError() == SSL_IDMISMATCH

    Check Documentation of SslError for all error-types.

    EDIT: I tested the function on my own self-cert server (its a Xampp), and I got Error #3.
    That means you have to check for error.hasError(SslError.SSL_UNTRUSTED) for a self-signed cert.

    based on documentation:

    Have you tried using the method getIssuedBy().getDName() of class SslCertificate. This method returns a String representing “The entity that issued this certificate”.

    Take a look here: http://developer.android.com/reference/android/net/http/SslCertificate.html#getIssuedBy()

    Then you just need to know wich string is returned when it is self signed.

    EDIT: I think that if it is selfsigned, that should return empty string, and if not, it would return the entity

    Regards

    Android Babe is a Google Android Fan, All about Android Phones, Android Wear, Android Dev and Android Games Apps and so on.