如何在Webview上接收SslError被调用时获取证书指纹



我的应用程序使用 https 连接到服务器,对于使用自签名证书的服务器,webview 将调用其 onReceivedSslError 方法。我需要响应回调并显示一个可见的对话框来显示证书信息,尤其是 sha1 和 sha256 指纹。但是我对获得它们感到非常困惑,因为SslCertificate不支持任何getFingerprint方法。有什么想法吗?

onReceivedSslError 方法如下所示。

 @Override
    public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {
        Log.d(DEBUG_TAG, "onReceivedSslError " + error.getCertificate().toString());
        // error.getCertificate().getSha1FingerPrint(); is not available
        // below methods are available
        // error.getCertificate().getIssuedBy();
        // error.getCertificate().getIssuedTo();
        // error.getCertificate().getValidNotAfterDate();
        // error.getCertificate().getValidNotBeforeDate();
        // error.getUrl();
        if (error.getPrimaryError() == SslError.SSL_UNTRUSTED) {
            final Account account = new Account(serverUrl, null, null);
            SslConfirmDialog dialog = new SslConfirmDialog(account,
                    new SslConfirmDialog.Listener() {
                        @Override
                        public void onAccepted(boolean rememberChoice) {
                            CertsManager.instance().saveCertForAccount(account, rememberChoice);
                            // Ignore SSL certificate validate
                            handler.proceed();
                        }
                        @Override
                        public void onRejected() {
                            displaySSLError();
                        }
                    });
            dialog.show(getSupportFragmentManager(), SslConfirmDialog.FRAGMENT_TAG);
            return;
        }
    }

您可以执行此类操作来获取 X509 证书

public void onReceivedSslError(WebView view, SslErrorHandler handler,
        SslError error) {
    Bundle bundle = SslCertificate.saveState(error.getCertificate());
    X509Certificate certificate;
    byte[] bytes = bundle.getByteArray("x509-certificate");
    if (bytes == null) {
        certificate = null;
    } else {
        try {
            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
            Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
            certificate = (X509Certificate) cert;
        } catch (CertificateException e) {
            certificate = null;
        }
    }
}

拥有 X509Certificate 后,您可以使用 X509TCertificate 的 getEncoded() API 获取指纹。

礼貌 : http://grepcode.com/file/repo1.maven.org/maven2/org.robolectric/android-all/4.3_r2-robolectric-0/android/net/http/SslCertificate.java#SslCertificate.restoreState%28android.os.Bundle%29

SslCertificate 类没有底层 X509TCertificate 的公共获取器,我们只能通过 hack 来做到这一点。这仅适用于 andorid 4.0+。所以我通过下面的一个小技巧解决了这个问题。

 /**
 * SslCertificate class does not has a public getter for the underlying
 * X509Certificate, we can only do this by hack. This only works for andorid 4.0+
 * @see https://groups.google.com/forum/#!topic/android-developers/eAPJ6b7mrmg
 */
public static X509Certificate getX509CertFromSslCertHack(SslCertificate sslCert) {
    X509Certificate x509Certificate = null;
    Bundle bundle = SslCertificate.saveState(sslCert);
    byte[] bytes = bundle.getByteArray("x509-certificate");
    if (bytes == null) {
        x509Certificate = null;
    } else {
        try {
            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
            Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
            x509Certificate = (X509Certificate) cert;
        } catch (CertificateException e) {
            x509Certificate = null;
        }
    }
    return x509Certificate;
}

最新更新