安卓Webview客户端证书、双向认证、Webview上的SSL

5
我有一个网页需要进行相互认证才能加载页面。但是我收到了onReceivedError(),并且Webkit日志中打印出“无法建立安全连接”。我已经广泛搜索,但没有找到答案。有几篇帖子,但没有得出结论。我尝试了这里发布的三个解决方案。可能有效的解决方案是:解决方案1:仍然使用ClientCertRequestHandler(它被标记为隐藏,但似乎仍然可用):
我修改了android.jar以包含内部API,以覆盖onReceivedClientCertRequest()。但是在运行时我没有得到回调。任何第三方浏览器都是如此。我在标准浏览器中加载相同的网页。我得到了一个UI回调,要求用户选择客户端证书。
因此,似乎只有系统浏览器应用程序可以从Webkit获取onReceivedClientCertRequest()的回调。
在iOS平台的情况下,Webview也无法直接加载该站点。但是使用NSURL进行HTTPS连接会在内存中保留客户端证书一段时间,从而使Webview可以成功加载此页面。
在Android上,我已经通过注册加载客户端和服务器证书的SSLSocketFactory成功设置了HTTPS通信。我可以使用它进行REST API调用。但是,与iOS不同的是,我找不到Android webview可以使用客户端证书进行相互认证的方法。
我认为,在Webview上实现相互认证应该是平台上安全性的基本要求之一。这个问题有什么更新吗?
编辑1:
根据我下面给出的答案,我已经在Android 4.0到4.3上工作了。然而,现在在Android 4.4上,似乎WebViewClientClassicExt类本身被删除了。在这种情况下,有什么办法?为什么Android不允许在webview中设置ClientCertificates?
1个回答

3

我能让这个东西在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();
        }

    }

我对这个答案有两个问题。第一,WebViewClient.onReceivedClientCertRequest()方法是在API Level 21(Lolipop)中添加的,但你说你在Android 4.0和4.1上使用了这个方法。第二,你是否找到了Android 4.4及以上API级别的解决方案?请回复。 - Sagar Trehan
Sagar,我已经更新了第一部分的答案。我还没有检查过4.4以上的解决方案。 - Alok Kulkarni
嗨,我需要做同样的事情,但私钥在加密令牌中。我该如何使用它? - Indio
你需要查看如何从令牌中取出私钥,其余步骤应与上述相同。 - Alok Kulkarni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接