前言:? ? ?
????????因為我們的項目是Android + H5,之前的做法是把H5所需要的資源和html下載到本地這樣證書校驗不會走系統(tǒng)瀏覽器層只需要項目中預埋根證書就可以了,但是如果用webview加載線上的域名自簽名證書就會走系統(tǒng)級校驗在onReceivedSslError中返回ssl證書不受信,從而導致出現(xiàn)白頁的情況。
本篇文章貴在直接給提供一個工具類按照步驟直接使用:
使用方法:
1、在自己app的gradle中添加okhttp依賴,項目中如有可以忽略?
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
implementation 'com.squareup.okio:okio:1.14.0'
implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.11.0'
2、把服務器端給你的CA根證書(.cer后綴的,貌似.pem的不行但是自己改一下后綴好像可以用)放到自己的assets目錄下
3、在自己的webview中setWebViewClient的時候重寫一下onReceivedSslError方法,在該方法中處理ssl握手失敗的場景可以通過打印error.getPrimaryError()去SslError類中對比一下報錯原因
mWebview.setWebViewClient(new WebViewClient() {
...
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
// super.onReceivedSslError(view, handler, error);
//自簽名證書校驗失敗
new CheckoutServerCaUtil().checkoutServerCA(handler,view.getUrl(),view.getContext(),"chain.cer");
}
}
4、使用提供的工具類直接校驗,工具如下創(chuàng)建名為CheckoutServerCaUtil的類直接ctrl + v 粘貼調(diào)用checkoutServerCA方法,參數(shù)參考工具中的注釋部分就可完成證書的校驗和主機名校驗:文章來源:http://www.zghlxwxcb.cn/news/detail-536501.html
package com.citicbank.cbframeworkcore.util;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;
import android.webkit.SslErrorHandler;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
* author : jsxin
* e-mail : jsxin0816@163.com
* time : 2022/11/17
* desc : 分行自簽名證書校驗工具:
* 功能:1、根證書校驗 2、主機名校驗
* 用法:自己的webview.setWebViewClient(new WebViewClient() {
* new CheckoutServerCaUtil().checkoutServerCA(handler,view.getUrl(),view.getContext());
* }
*/
public class CheckoutServerCaUtil {
private final String TAG = "CheckoutServerCaUtil";
public CheckoutServerCaUtil() {
}
/**
* 根證書校驗:onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
* @param handler : onReceivedSslError回調(diào):SslErrorHandler
* @param url : onReceivedSslError回調(diào):WebView.getUrl
* @param context: onReceivedSslError回調(diào):WebView.getContext
* @param caName: 放在assets目錄下的根證書名稱:例如:"chain.cer"
*/
public void checkoutServerCA(final SslErrorHandler handler, String url, Context context,String caName) {
OkHttpClient.Builder builder;
try {
InputStream inputStream = context.getAssets().open(caName);
Log.d(TAG,"jsxin--->>>執(zhí)行:--->>>00--->>>url:" + url);
builder = setCertificates(new OkHttpClient.Builder(), inputStream);
} catch (IOException e) {
Log.d(TAG,"jsxin--->>>異常:--->>>01");
builder = new OkHttpClient.Builder();
}
Request request = new Request.Builder().url(url)
.build();
builder.build().newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException error) {
Log.d(TAG,"jsxin--->>>自簽名證書校驗結(jié)果:---error:----" + error.toString());
handler.cancel();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG,"jsxin--->>>自簽名證書校驗結(jié)果:---onResponse:----" + response.code());
handler.proceed();
}
});
}
private OkHttpClient.Builder setCertificates(OkHttpClient.Builder client, InputStream... certificates) {
try {
Log.d(TAG,"jsxin--->>>執(zhí)行:--->>>01");
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) {
Log.d(TAG,"jsxin--->>>異常:--->>>02");
e.printStackTrace();
}
}
Log.d(TAG,"jsxin--->>>執(zhí)行:--->>>02");
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
Log.d(TAG,"jsxin--->>>異常:--->>>03");
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
Log.d(TAG,"jsxin--->>>執(zhí)行:--->>>03");
client.sslSocketFactory(sslSocketFactory, trustManager);
hostNameVerifier(client);
} catch (Exception e) {
Log.d(TAG,"jsxin--->>>異常:--->>>04");
e.printStackTrace();
}
return client;
}
private void hostNameVerifier(OkHttpClient.Builder client) {
Log.d(TAG,"jsxin--->>>執(zhí)行:--->>>04");
client.hostnameVerifier(new HostnameVerifier() {
@SuppressLint("BadHostnameVerifier")
@Override
public boolean verify(String hostname, SSLSession session) {
String peerHost = session.getPeerHost();//服務器返回的域名
Log.d(TAG,"jsxin--->>>執(zhí)行:--->>>05--->>>服務器返回域名 peerHost:" + peerHost + "--->>>主機名:" + hostname);
try {
X509Certificate[] peerCertificates = (X509Certificate[]) session.getPeerCertificates();
for (X509Certificate c : peerCertificates) {
X500Principal subjectX500Principal = c.getSubjectX500Principal();
// String name = new X500Principal(subjectX500Principal).getName();
String name = subjectX500Principal.getName();
Log.d(TAG,"jsxin--->>>執(zhí)行:--->>>06--->>>subjectX500Principal.getName():" + name);
String[] split = name.split(",");
for (String s : split) {
if (s.startsWith("CN")) {
if (s.contains(hostname) && s.contains(peerHost)) {
Log.d(TAG,"jsxin--->>>執(zhí)行:--->>>07");
return true;
}
}
}
}
} catch (SSLPeerUnverifiedException e) {
Log.d(TAG,"jsxin--->>>異常:--->>>05");
e.printStackTrace();
}
Log.d(TAG,"jsxin--->>>主機名校驗失敗");
return false;
}
});
}
}
如果想系統(tǒng)的學習一下可以參考這個鏈接,網(wǎng)上也基本上都是抄的這個但是里面有的一些方法過時了,所以可以直接梭哈我的工具:Android Webview SSL 自簽名安全校驗解決方案 - 熠然 - 博客園文章來源地址http://www.zghlxwxcb.cn/news/detail-536501.html
到了這里,關于Android中使用Webview SSL 自簽名CA證書安全校驗方案的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!