【问题标题】:Android Webview Client Certificate, Mutual Auth, SSL over WebviewAndroid Webview 客户端证书、相互验证、基于 Webview 的 SSL
【发布时间】:2014-01-11 10:43:36
【问题描述】:

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

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

看来只有系统浏览器应用程序才能从 Webkit 获取 onReceivedClientCertRequest() 的回调。

iOS 平台的情况下,Webview 也不能直接加载站点。但是使用 NSURL 进行 HTTPS 连接, 将Client证书在内存中保存一段时间,Webview可以成功加载此页面。

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

我认为平台应该支持在 Webview 上进行 Mutual auth 作为安全性的基本要求之一。这个问题有更新吗?

编辑 1

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

【问题讨论】:

    标签: android ssl https webview


    【解决方案1】:

    所以我可以让这个东西工作到 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();
            }
    
        }
    

    【讨论】:

    • 我对这个答案有 2 个疑问。一。方法 WebViewClient.onReceivedClientCertRequest() 是在 API Level 21(Lolipop) 中添加的,但是你说你在 Android 4.0 和 4.1 上使用过这个方法。第二。您是否找到适用于 Android 4.4 及更高 API 级别的解决方案。请回复?
    • Sagar 我已经更新了第一部分的答案。我还没有检查 4.4 以上的解决方案。
    • 嗨,我也必须这样做,但私钥在加密令牌中。我可以用吗?
    • 您需要检查如何从令牌中取出私钥,其余步骤与上述相同
    猜你喜欢
    • 2013-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-18
    • 2014-05-31
    相关资源
    最近更新 更多