GooglePlay SSL Error Handler

应用上架GooglePlay 收到邮件提示 For more information on how to address WebView SSL Error Handler alerts, please see this Google Help Center article.

出现这个原因是因为我在app中使用webview加载Https的H5界面,在onReceivedSslError()中处理SslErrorHandler时,出现白屏现象,原因是webview默认在加载有证书验证的url时,会默认使用handler.cancel()进行拦截操作,这里只需要改成handler.proceed()就可以解决白屏问题了。

但是,apk上传GooglePlay时会检测到“这种实施方式会忽略所有 SSL 证书验证错误,使应用容易受到中间人攻击。攻击者可能会更改受影响的 WebView 内容、读取传输的数据(例如登录凭据),以及执行应用中使用 JavaScript 的代码”(详情请点击进入https://support.google.com/faqs/answer/7071387

解决方案:

1、检测到异常时,弹出提示框,提示用户(本人没有亲测是否可以上传GooglePlay)

public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        final SslErrorHandler mHandler ;
        mHandler= handler;
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setMessage("ssl证书验证失败");
        builder.setPositiveButton("继续", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                mHandler.proceed();
            }
        });
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                mHandler.cancel();
            }
        });
        builder.setOnKeyListener(new DialogInterface.OnKeyListener() {
            @Override
            public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
                if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
                    mHandler.cancel();
                    dialog.dismiss();
                    return true;
                }
                return false;
            }
        });
        AlertDialog dialog = builder.create();
        dialog.show();
    }

2、步骤繁杂,亲测可提交GooglePlay。

  1. 从网站上检索证书(可以参考:https://blog.csdn.net/qq_35229064/article/details/88250392
  2. 将证书(.cer文件)复制到应用程序的res / raw文件夹中

       在代码中,通过调用LoadSslCertificate()加载证书

private static final int[] CERTIFICATES = {
        R.raw.证书名,   // you can put several certificates
};
private ArrayList<SslCertificate> certificates = new ArrayList<>();

private void loadSSLCertificates() {
    try {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        for (int rawId : CERTIFICATES) {
            InputStream inputStream = getResources().openRawResource(rawId);
            InputStream certificateInput = new BufferedInputStream(inputStream);
            try {
                Certificate certificate = certificateFactory.generateCertificate(certificateInput);
                if (certificate instanceof X509Certificate) {
                    X509Certificate x509Certificate = (X509Certificate) certificate;
                    SslCertificate sslCertificate = new SslCertificate(x509Certificate);
                    certificates.add(sslCertificate);
                } else {
                    Log.w(TAG, "Wrong Certificate format: " + rawId);
                }
            } catch (CertificateException exception) {
                Log.w(TAG, "Cannot read certificate: " + rawId);
            } finally {
                try {
                    certificateInput.close();
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    } catch (CertificateException e) {
        e.printStackTrace();
    }
}

发生SslError时,请检查服务器证书是否与一个嵌入式证书匹配。请注意,无法直接比较证书,因此我使用SslCertificate.saveState将证书数据放入Bundle,然后我比较所有bundle条目

webView.setWebViewClient(new WebViewClient() {

    @Override
    public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {

        // Checks Embedded certificates
        SslCertificate serverCertificate = error.getCertificate();
        Bundle serverBundle = SslCertificate.saveState(serverCertificate);
        for (SslCertificate appCertificate : certificates) {
            if (TextUtils.equals(serverCertificate.toString(), appCertificate.toString())) { // First fast check
                Bundle appBundle = SslCertificate.saveState(appCertificate);
                Set<String> keySet = appBundle.keySet();
                boolean matches = true;
                for (String key : keySet) {
                    Object serverObj = serverBundle.get(key);
                    Object appObj = appBundle.get(key);
                    if (serverObj instanceof byte[] && appObj instanceof byte[]) {     // key "x509-certificate"
                        if (!Arrays.equals((byte[]) serverObj, (byte[]) appObj)) {
                            matches = false;
                            break;
                        }
                    } else if ((serverObj != null) && !serverObj.equals(appObj)) {
                        matches = false;
                        break;
                    }
                }
                if (matches) {
                    handler.proceed();
                    return;
                }
            }
        }

        handler.cancel();
        String message = "SSL Error " + error.getPrimaryError();
        Log.w(TAG, message);
    }


});

然后就可以测试结果了

(网上搜索加自己理解,请大家多多指点)


版权声明:本文为qq_35229064原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。