Gre*_*tor 3 android ca ssl-certificate x509certificate android-webview
根据安全扫描程序的结果,我需要限制应用程序信任的证书颁发机构。
扫描结果指出 处的线webView.loadUrl("https://example.com/page");。我看到了如何创建一个使用我的 TrustManager 的 SslSocketFactory,但我在 WebView 中没有看到允许我设置它的 API。
https://developer.android.com/training/articles/security-ssl.html#UnknownCa
有哪些可能的方法来实现这一目标?
我认为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);
}
Run Code Online (Sandbox Code Playgroud)
然后,扩展自定义 WebViewClient 类,检查来自/sf/answers/446560411/ 的片段
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();
}
}
Run Code Online (Sandbox Code Playgroud)
最后,设置CheckServerTrustedWebViewClient为WebView
webView.setWebViewClient(new CheckServerTrustedWebViewClient());
Run Code Online (Sandbox Code Playgroud)
然而,有一个问题。准备好的 CA 证书是服务器的确切签名(中间 CA 不是根 CA)。只提供根 CA 证书是行不通的。TrustManager 不是可以在运行时下载服务器证书链吗?有什么建议吗?