WebView loadUrl()中的信任证书颁发机构

时间:2016-10-31 19:23:16

标签: android android-webview ssl-certificate x509certificate ca

从安全扫描程序的结果来看,我需要限制应用程序信任的证书颁发机构。

扫描结果指出webView.loadUrl("https://example.com/page");处的行。我看到如何创建一个使用我的TrustManager的SslSocketFactory,但我没有在WebView中看到允许我设置它的API。

https://developer.android.com/training/articles/security-ssl.html#UnknownCa

有哪些可能的方法来实现这一目标?

1 个答案:

答案 0 :(得分:3)

我认为WebViewClient' onReceivedSslError方法将是一个很好的切入点。

首先,请遵循https://developer.android.com/training/articles/security-ssl.html#UnknownCa中完全相同的代码段来准备TrustManager。

    TrustManagerFactory tmf = null;

    private void initTrustStore() throws
            java.security.cert.CertificateException, FileNotFoundException,
            IOException, KeyStoreException, NoSuchAlgorithmException {

        // Create a KeyStore containing our trusted CAs
        String keyStoreType = KeyStore.getDefaultType();
        KeyStore trustedKeyStore = KeyStore.getInstance(keyStoreType);
        trustedKeyStore.load(null, null);

        CertificateFactory cf = CertificateFactory.getInstance("X.509");

        InputStream caInput = new BufferedInputStream(
                    getResources().getAssets().open("ca.crt"));
            Certificate ca;
            try {
                ca = cf.generateCertificate(caInput);
                Log.d(TAG, "ca-root DN=" + ((X509Certificate) ca).getSubjectDN());
            }
            finally {
                caInput.close();
            }
            trustedKeyStore.setCertificateEntry("ca", ca);

        // Create a TrustManager that trusts the CAs in our KeyStore
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(trustedKeyStore);

    }

然后,扩展自定义WebViewClient类,从https://stackoverflow.com/a/6379434/1099884

检查代码段
private class CheckServerTrustedWebViewClient extends WebViewClient{
    public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {
        Log.d(TAG, "onReceivedSslError");
        boolean passVerify = false;

        if(error.getPrimaryError() == SslError.SSL_UNTRUSTED){
            SslCertificate cert = error.getCertificate();
            String subjectDN = cert.getIssuedTo().getDName();
            Log.d(TAG, "subjectDN: "+subjectDN);
            try{
                Field f = cert.getClass().getDeclaredField("mX509Certificate");
                f.setAccessible(true);
                X509Certificate x509 = (X509Certificate)f.get(cert);

                X509Certificate[] chain = {x509};
                for (TrustManager trustManager: tmf.getTrustManagers()) {
                    if (trustManager instanceof X509TrustManager) {
                        X509TrustManager x509TrustManager = (X509TrustManager)trustManager;
                        try{
                            x509TrustManager.checkServerTrusted(chain, "generic");
                            passVerify = true;break;
                        }catch(Exception e){
                            Log.e(TAG, "verify trustManager failed", e);
                            passVerify = false;
                        }
                    }
                }
                Log.d(TAG, "passVerify: "+passVerify);
            }catch(Exception e){
                Log.e(TAG, "verify cert fail", e);
            }
        }
        if(passVerify == true)handler.proceed();
        else handler.cancel();

    }       

}

最后,将CheckServerTrustedWebViewClient设置为WebView

webView.setWebViewClient(new CheckServerTrustedWebViewClient());

然而,有一个问题。准备好的CA证书是服务器1的正确签名(中间CA非根CA)。仅提供根CA证书将不起作用。不是TrustManager可以在运行时下载服务器证书链吗?有什么建议吗?