Android中的WebView一般使用就是加载一个地址或者进行一些交互等等的操作,这些基本没什么难度。工作中有接触到了关于证书的一些操作,这里做一些记录。
1、WebView请求时携带证书
当我们使用的WebView需要进行证书校验的时候,我们需要把证书携带上。那我们怎么样才能携带我们的证书呢。
我们就需要使用 WebViewClient 了,当需要证书校验的时候,会执行 WebViewClient 的onReceivedClientCertRequest 方法。
@Override
public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
try {
PrivateKey privateKey = null;
X509Certificate[] certificates = null;
InputStream certificateFileStream = new ByteArrayInputStream(Base64.decode("您的证书内容", Base64.DEFAULT));
KeyStore keyStore = KeyStore.getInstance("证书类型");
String password = "您的证书密码";
keyStore.load(certificateFileStream, password.toCharArray());
Enumeration<String> aliases = keyStore.aliases();
String alias = aliases.nextElement();
Key key = keyStore.getKey(alias, password.toCharArray());
if (key instanceof PrivateKey) {
privateKey = (PrivateKey) key;
Certificate cert = keyStore.getCertificate(alias);
certificates = new X509Certificate[1];
certificates[0] = (X509Certificate) cert;
}
certificateFileStream.close();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
request.proceed(privateKey, certificates);
}
} catch (Exception e) {
}
}
2、双向ssl认证
这里就不做多的介绍了,可以参考这篇文章https://blog.csdn.net/fengdezhudi/article/details/53436565,
作者介绍的很详细,感谢作者
package com.zx.webview_ssl;
import android.annotation.TargetApi;
import android.net.Uri;
import android.os.Build;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
public class SslWebViewClient extends WebViewClient {
private SSLContext sslContext;
public SslWebViewClient() {
try {
TrustManager[] trustManagers = prepareTrustManager(MyApplication.mContext.getResources().getAssets().open("zx_server.cer"));
KeyManager[] keyManagers = prepareKeyManager(MyApplication.mContext.getResources().getAssets().open("zx_client.bks"), "123456");
sslContext = SSLContext.getInstance("TLS");
X509TrustManager trustManager = null;
if (null != trustManagers){
trustManager = new MyTrustManager(chooseTrustManager(trustManagers));
}else {
trustManager = new UnSafeTrustManager();
}
sslContext.init(keyManagers, new TrustManager[]{trustManager}, new SecureRandom());
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
return processRequest(Uri.parse(url));
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
return processRequest(request.getUrl());
}
private WebResourceResponse processRequest(Uri uri) {
try {
//设置连接
URL url = new URL(uri.toString());
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
//为request设置SSL Socket Factory
urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());
urlConnection.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
//获取请求的内容、contentType、encoding
InputStream inputStream = urlConnection.getInputStream();
String contentType = urlConnection.getContentType();
String encoding = urlConnection.getContentEncoding();
if (null != contentType){
String mimeType = contentType;
if (contentType.contains(";")){
mimeType = contentType.split(";")[0].trim();
}
//返回新的response
return new WebResourceResponse(mimeType, encoding, inputStream);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private TrustManager[] prepareTrustManager(InputStream... certificates) {
if (certificates == null || certificates.length <= 0){
return null;
}
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try {
if (certificate != null)
certificate.close();
} catch (IOException e){
}
}
TrustManagerFactory trustManagerFactory = null;
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
return trustManagers;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private KeyManager[] prepareKeyManager(InputStream bksFile, String password) {
try {
if (bksFile == null || password == null){
return null;
}
KeyStore clientKeyStore = KeyStore.getInstance("BKS");
clientKeyStore.load(bksFile, password.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, password.toCharArray());
return keyManagerFactory.getKeyManagers();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers) {
for (TrustManager trustManager : trustManagers) {
if (trustManager instanceof X509TrustManager) {
return (X509TrustManager) trustManager;
}
}
return null;
}
public static class MyTrustManager implements X509TrustManager{
private X509TrustManager defaultTrustManager;
private X509TrustManager localTrustManager;
public MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException {
TrustManagerFactory var4 = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
var4.init((KeyStore) null);
defaultTrustManager = chooseTrustManager(var4.getTrustManagers());
this.localTrustManager = localTrustManager;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ce) {
localTrustManager.checkServerTrusted(chain, authType);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
public static class UnSafeTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
}
3、设置cookie
因为有些地址需要我们提前设置cookie才能正确的访问,所以我们在loadUrl之前需要将cookie设置进去
public static void fillCookie(String url, Map<String, String> cookieMap) {
CookieManager cookieManager = CookieManager.getInstance();
CookieManager.getInstance().removeSessionCookies(value -> {
});
CookieManager.getInstance().setAcceptCookie(true);
Set<String> keySet = cookieMap.keySet();
String domain = "." + URLUtils.getDomainName(url);
for (String key : keySet) {
cookieManager.setCookie(domain, key+"="+cookieMap.get(key));
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
CookieSyncManager.getInstance().sync();
} else {
cookieManager.flush();
}
}
fillCookie方法自动进行填充cookie,url是请求地址,cookieMap对应的是cookie的名称和具体参数,因为cookie会对应域名,所以我们解析了域名,下面是域名解析的工具类。
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
public class URLUtils {
private final static Set<String> PublicSuffixSet = new HashSet<>(Arrays.asList("com|org|net|gov|edu|co|tv|mobi|info|asia|xxx|onion|cn|com.cn|edu.cn|gov.cn|net.cn|org.cn|jp|kr|tw|com.hk|hk|com.hk|org.hk|se|com.se|org.se".split("\\|")));
private static Pattern IP_PATTERN = Pattern.compile("(\\d{1,3}\\.){3}(\\d{1,3})");
/**
* 获取url的顶级域名
*
* @param url
* @return
*/
public static String getDomainName(URL url) {
String host = url.getHost();
if (host.endsWith("."))
host = host.substring(0, host.length() - 1);
if (IP_PATTERN.matcher(host).matches())
return host;
int index = 0;
String candidate = host;
for (; index >= 0; ) {
index = candidate.indexOf('.');
String subCandidate = candidate.substring(index + 1);
if (PublicSuffixSet.contains(subCandidate)) {
return candidate;
}
candidate = subCandidate;
}
return candidate;
}
/**
* 获取url的顶级域名
*
* @param url
* @return
* @throws MalformedURLException
*/
public static String getDomainName(String url) {
try {
return getDomainName(new URL(url));
} catch (Exception e) {
return URI.create(url).getHost();
}
}
}
以上是一些工作时的记录,如果哪里有不对的地方请大家指正一下。