Android Webview 客户端证书、相互身份验证、SSL over Webview

标签 android ssl https webview

我有一个网页需要相互验证才能加载该页面。 我收到带有 ERROR_FAILED_SSL_HANDSHAKE 的 onReceivedError()。在日志中,“无法建立安全连接”由 Webkit 打印。 我进行了广泛的搜索,但找不到答案。有几个帖子,但没有定论。 我尝试了发布的所有 3 个解决方案 here . 可能有效的解决方案是:- 解决方案 1: 无论如何都使用 ClientCertRequestHandler(它被标记为隐藏,但显然仍然可用):

所以我修改了 android.jar 以包含用于覆盖 onReceivedClientCertRequest() 的内部 API 但是我在运行时没有得到回调。任何第三方浏览器也是如此。我尝试在标准浏览器中加载相同的网页。我在 UI 上收到回调,要求用户选择客户端证书。

似乎只有系统浏览器应用程序可以从Webkit 获取onReceivedClientCertRequest() 的回调。

如果是 iOS 平台,Webview 也不能直接加载站点。但是使用 NSURL 建立 HTTPS 连接, 将客户端证书保留在内存中一段时间​​,Webview 可以成功加载此页面。

在 Android 上,我通过注册加载客户端和服务器证书的 SSLSocketFactory 成功设置了 HTTPS 通信。 我可以使用它进行 REST API 调用。但是与 iOS 不同,我找不到 Android webview 可以使用客户端证书进行相互身份验证的方法。

我认为平台应该支持通过 Webview 进行相互身份验证,作为安全性的基本要求之一。这个问题有更新吗?

编辑 1:

根据下面给出的答案,我让它在 Android 4.0 到 4.3 上运行。 Hoverer,现在在 Android 4.4 上,似乎 WebViewClientClassicExt 类本身已被删除。 知道在这种情况下可以做什么吗?为什么 Android 不允许在 webview 中设置 ClientCertificates?

最佳答案

所以我可以让这个东西工作到 4.3

  1. 在 Android 4.0 和 4.1 上,通过覆盖扩展 WebViewClient 的类的 onReceivedClientCertRequest()
  2. 在 4.2 和 4.3 上,重写扩展 WebViewClientClassicExt 的类的 onReceivedClientCertRequest()。

我设置了私钥和证书 ClientCertRequestHandler proceed() 方法。

需要 Android 4.4 及更高版本的修复补丁

已编辑 下面给出了 4.3 之前的解决方案

WebviewClientCustom.java

public class WebViewClientCustom extends WebViewClient {
    private X509Certificate[] certificatesChain;
    private PrivateKey clientCertPrivateKey;
    private IWebViewCallbacks webviewCallbacks;

    public WebViewClientCustom(IWebViewCallbacks webviewCallbacks) {
        this.webviewCallbacks = webviewCallbacks;
    }

    public void onReceivedClientCertRequest(WebView paramWebView,
            ClientCertRequestHandler paramClientCertRequestHandler,
            String paramString) {
        PrivateKey localPrivateKey = this.clientCertPrivateKey;
        X509Certificate[] arrayOfX509Certificate = this.certificatesChain;
        paramClientCertRequestHandler.proceed(localPrivateKey,
                arrayOfX509Certificate);
    }

    public void onReceivedError(WebView view, int errorCode,
            String description, String failingUrl) {
        webviewCallbacks.onReceivedError( view,  errorCode,
                 description,  failingUrl);

        super.onReceivedError( view,  errorCode,
                 description,  failingUrl);
    }


    public void setClientCertificate(PrivateKey paramPrivateKey,
            X509Certificate[] paramArrayOfX509Certificate) {
        this.clientCertPrivateKey = paramPrivateKey;
        this.certificatesChain = paramArrayOfX509Certificate;
    }

    public boolean shouldOverrideUrlLoading(WebView paramWebView,
            String paramString) {
        return webviewCallbacks.shouldOverrideUrlLoading(paramWebView,
                paramString);


    }

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        // TODO Auto-generated method stub

        webviewCallbacks.onPageStarted(view, url, favicon);
        super.onPageStarted(view, url, favicon);
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        // TODO Auto-generated method stub

        webviewCallbacks.onPageFinished(view, url);
        super.onPageFinished(view, url);
    }

}

WebViewClientCustomExt.java

public class WebViewClientCustomExt extends WebViewClientClassicExt {
    private X509Certificate[] certificatesChain;
    private PrivateKey clientCertPrivateKey;
    private IWebViewCallbacks webviewCallbacks;

    public WebViewClientCustomExt(IWebViewCallbacks webviewCallbacks) {
        this.webviewCallbacks = webviewCallbacks;
    }

    public void onReceivedClientCertRequest(WebView paramWebView,
            ClientCertRequestHandler paramClientCertRequestHandler,
            String paramString) {
        PrivateKey localPrivateKey = this.clientCertPrivateKey;
        X509Certificate[] arrayOfX509Certificate = this.certificatesChain;
        paramClientCertRequestHandler.proceed(localPrivateKey,
                arrayOfX509Certificate);
    }

    public void onReceivedError(WebView view, int errorCode,
            String description, String failingUrl) {
        webviewCallbacks.onReceivedError( view,  errorCode,
                 description,  failingUrl);

        super.onReceivedError( view,  errorCode,
                 description,  failingUrl);
    }



    public void setClientCertificate(PrivateKey paramPrivateKey,
            X509Certificate[] paramArrayOfX509Certificate) {
        this.clientCertPrivateKey = paramPrivateKey;
        this.certificatesChain = paramArrayOfX509Certificate;
    }

    public boolean shouldOverrideUrlLoading(WebView paramWebView,
            String paramString) {
        return webviewCallbacks.shouldOverrideUrlLoading(paramWebView, paramString);
    }

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        // TODO Auto-generated method stub


        webviewCallbacks.onPageStarted(view, url, favicon);
        super.onPageStarted(view, url, favicon);
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        // TODO Auto-generated method stub

        webviewCallbacks.onPageFinished(view, url);
        super.onPageFinished(view, url);
    }
}

用法

 */
    private void setCertificateData() {
        // TODO Auto-generated method stub
        try {
            KeyStore clientCertKeystore = KeyStore.getInstance("pkcs12");
            String clientCertPkcsPassword = getPkcsPassword();
            byte[] pkcs12;

            pkcs12 = getAuthP12Data();
            ByteArrayInputStream pkcs12BAIS = new ByteArrayInputStream(pkcs12);

            clientCertKeystore.load(pkcs12BAIS,
                    clientCertPkcsPassword.toCharArray());
            String alias = (clientCertKeystore.aliases().nextElement());
            Certificate[] arrayOfCertificate = clientCertKeystore
                    .getCertificateChain(alias);
            X509Certificate[] arrayOfX509Certificate = new X509Certificate[arrayOfCertificate.length];
            for (int i = 0; i < arrayOfCertificate.length; i++) {
                arrayOfX509Certificate[i] = (X509Certificate) arrayOfCertificate[i];
            }
            PrivateKey localPrivateKey = (PrivateKey) clientCertKeystore
                    .getKey(alias, clientCertPkcsPassword.toCharArray());
            if (android.os.Build.VERSION.SDK_INT <= 16) {
                WebViewClientCustom webvviewClient = new WebViewClientCustom(
                        myWebViewClient);
                webvviewClient.setClientCertificate(localPrivateKey,
                        arrayOfX509Certificate);
                webView.setWebViewClient(webvviewClient);

            } else {
                WebViewClientCustomExt webvviewClient = new WebViewClientCustomExt(
                        myWebViewClient);
                webvviewClient.setClientCertificate(localPrivateKey,
                        arrayOfX509Certificate);
                webView.setWebViewClient(webvviewClient);
            }
            // webView.getSettings().setJavaScriptEnabled(true);

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

关于Android Webview 客户端证书、相互身份验证、SSL over Webview,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20739727/

相关文章:

android - 应用程序在获取已安装软件包的名称时意外停止

linux - uWSGI 配置使用 HTTPS

ruby-on-rails - 在firefox中网站被重定向到 "https"(其他浏览器正常)

android - 如何在工具栏中制作圆角菜单?

android - 用户没有资格购买此 android inapp 购买

android - 如何在 Bottombar 上方显示 Snackbar?

iis - 未安装 SSL 证书

ruby - 如何为本地主机上的多个域和子域使用 HTTPS?

python - 如何获取证书的 SHA1 指纹

docker - Traefik https不完全安全