Android WebView是Android提供的一个组件,用于在应用中显示网页内容。其内部使用了WebKit引擎来解析和渲染网页。在使用WebView加载HTTPS网页时,可能会遇到证书认证失败的情况,本文将对此问题进行原理解析和详细介绍。
一、HTTPS和证书认证简介
HTTPS是HTTP的安全版本,通过使用SSL/TLS协议对通信内容进行加密,保证数据传输的安全性。证书认证是HTTPS协议中常用的一种安全机制,用于验证服务器的身份。当客户端发起HTTPS请求时,服务器会返回一个证书,证书中包含了服务器的公钥和其他相关信息。客户端根据证书中的公钥验证服务器的身份,并使用该公钥进行加密通信。
二、证书认证失败的原因
1. 证书过期:证书通常都有一个有效期限,当证书过期后,浏览器将不再信任该证书。
2. 证书颁发者不受信任:浏览器内置了一些受信任的证书颁发机构(CA),只有由这些CA颁发的证书才被认为是可信任的。
3. 主机名不匹配:证书中会包含服务器的主机名信息,浏览器会检查证书中的主机名和实际访问的主机名是否匹配,如果不匹配则认为是不安全的。
三、Android WebView中的证书认证机制
Android WebView对证书认证有自己的默认行为,根据不同的情况采取不同的策略。
1. 默认行为:如果证书过期或由不受信任的颁发机构颁发,则WebView将抛出SSLException,停止加载网页。
2. 信任所有证书:可以通过设置WebView的WebSettings属性来信任所有证书,不进行证书认证。这种方式会造成安全风险,不建议使用。
```java
webSettings.setAcceptThirdPartyCookies(false);
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed();
}
});
```
3. 自定义证书认证:可以通过自定义WebViewClient的方式来处理证书认证,实现自己的逻辑。
```java
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
// 自定义证书认证逻辑,比如检查证书是否过期、是否受信任等
// 最后根据判断结果调用 handler.proceed() 或 handler.cancel(),决定是否允许加载网页
}
});
```
四、自定义证书认证的实现
自定义证书认证通常需要用到Android中的X509TrustManager接口和SSLSocketFactory类。
1. 创建自定义的X509TrustManager:
```java
public class CustomTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// 客户端证书认证逻辑,可以留空
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// 服务器证书认证逻辑
for (X509Certificate certificate : chain) {
// 检查证书是否过期、是否受信任等
// 如果认证失败,可以抛出 CertificateException 异常
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
```
2. 创建自定义的SSLSocketFactory:
```java
public class CustomSSLSocketFactory extends SSLSocketFactory {
private SSLSocketFactory delegate;
public CustomSSLSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new CustomTrustManager()}, new SecureRandom());
delegate = sslContext.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return delegate.createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket(String host, int port) throws IOException {
return delegate.createSocket(host, port);
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
return delegate.createSocket(host, port, localHost, localPort);
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return delegate.createSocket(host, port);
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return delegate.createSocket(address, port, localAddress, localPort);
}
}
```
3. 在自定义的WebViewClient中使用自定义的SSLSocketFactory:
```java
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new CustomTrustManager()}, new SecureRandom());
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
handler.proceed();
view.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
return new WebResourceResponse(null, null, null);
}
});
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
}
});
```
通过自定义证书认证机制,我们可以自由地处理证书认证失败的情况,实现更加灵活的WebView加载行为。但是需要注意,自定义的证书认证逻辑要确保安全性,避免造成风险。
参考文献:
1. https://developer.android.com/guide/webapps/webview
2. https://developer.android.com/reference/android/net/http/SslError