Android webview手動校驗(yàn)https證書(by 星空武哥)
有些時(shí)候由于Android系統(tǒng)的bug或者其他的原因,導(dǎo)致我們的webview不能驗(yàn)證通過我們的https證書,最明顯的例子就是華為手機(jī)mate7升級到Android7.0后,手機(jī)有些網(wǎng)站打不開了,而更新了webview的補(bǔ)丁后就沒問題了,充分說明系統(tǒng)的bug對我們混合開發(fā)webview加載https地址的影響是巨大的。那么我們怎么去解決這個(gè)問題呢?

首先我們?nèi)シ治鲆幌鲁霈F(xiàn)的原因
當(dāng)webview加載https地址的時(shí)候,如果因?yàn)樽C書的問題出錯(cuò)的時(shí)候就會走onReceivedSslError()方法
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
}
}
而super.onReceivedSslError()默認(rèn)是

handler.cancel() 就是讓加載的頁面白屏,所有導(dǎo)致了如果webview校驗(yàn)證書存在異常,android在默認(rèn)情況下會顯示白屏,我們也可調(diào)用handler.proceed(),大多時(shí)候很多人都是這個(gè)處理,但是這也就意味著https證書失去了他存在的意義了。

那么如果你的網(wǎng)站證書是正常的,但是因?yàn)橄到y(tǒng)的bug導(dǎo)致了加載異常,這時(shí)候就需要我們手動校驗(yàn)了。
其實(shí)我們是可以手動校驗(yàn)網(wǎng)站證書的sha256,如果異常之后校驗(yàn)sha256就執(zhí)行handler.proceed(),失敗就退出應(yīng)用。
首先我們要獲取網(wǎng)站的證書
利用谷歌瀏覽器,打開網(wǎng)址并且按下“F12”,打開開發(fā)者模式

一步一步導(dǎo)出證書

然后在打開sha256校驗(yàn)網(wǎng)址:http://www.atool.org/file_hash.php
或http://tools.jb51.net/password/sha_encode

這樣就獲取到了證書的sha256的值,寫了一個(gè)工具類
/**
* SSL證書錯(cuò)誤,手動校驗(yàn)https證書
*
* @param cert https證書
* @param sha256Str sha256值
* @return true通過,false失敗
*/
public static boolean isSSLCertOk(SslCertificate cert, String sha256Str) {
byte[] SSLSHA256 = hexToBytes(sha256Str);
Bundle bundle = SslCertificate.saveState(cert);
if (bundle != null) {
byte[] bytes = bundle.getByteArray("x509-certificate");
if (bytes != null) {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate ca = cf.generateCertificate(new ByteArrayInputStream(bytes));
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] key = sha256.digest(((X509Certificate) ca).getEncoded());
return Arrays.equals(key, SSLSHA256);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return false;
}
/**
* hexString轉(zhuǎn)byteArr
* <p>例如:</p>
* hexString2Bytes("00A8") returns { 0, (byte) 0xA8 }
*
* @param hexString
* @return 字節(jié)數(shù)組
*/
public static byte[] hexToBytes(String hexString) {
if (hexString == null || hexString.trim().length() == 0)
return null;
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] bytes = new byte[length];
String hexDigits = "0123456789abcdef";
for (int i = 0; i < length; i++) {
int pos = i * 2; // 兩個(gè)字符對應(yīng)一個(gè)byte
int h = hexDigits.indexOf(hexChars[pos]) << 4; // 注1
int l = hexDigits.indexOf(hexChars[pos + 1]); // 注2
if (h == -1 || l == -1) { // 非16進(jìn)制字符
return null;
}
bytes[i] = (byte) (h | l);
}
return bytes;
}
然后在onReceivedSslError()判斷
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
if (error.getPrimaryError() == SslError.SSL_INVALID) {
// 如果手動校驗(yàn)sha256成功就允許加載頁面
if (SSLCertUtil.isSSLCertOk(error.getCertificate(), "6683c9584b8287ec3a50e312f4a540c79938aaeb76bd02e40a9ca037ee5d24f4")) {
handler.proceed();
} else {
try {
new AlertDialog.Builder(MainActivity.this)
.setTitle("警告")
.setMessage("證書校驗(yàn)失敗")
.setPositiveButton("退出", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
System.exit(0);
dialog.dismiss();
}
}).show();
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
handler.cancel();
}
}
});
這里我們只是真對SslError.SSL_INVALID進(jìn)行了判斷,可能還有其他情況,根據(jù)自己的情況判定。
/** * The certificate is not yet valid */ public static final int SSL_NOTYETVALID = 0; /** * The certificate has expired */ public static final int SSL_EXPIRED = 1; /** * Hostname mismatch */ public static final int SSL_IDMISMATCH = 2; /** * The certificate authority is not trusted */ public static final int SSL_UNTRUSTED = 3; /** * The date of the certificate is invalid */ public static final int SSL_DATE_INVALID = 4; /** * A generic error occurred */ public static final int SSL_INVALID = 5;
這樣就完成了手動校驗(yàn)https證書校
相關(guān)文章
Android組件間通信--深入理解Intent與IntentFilter
本篇文章是對Android組件間通信Intent與IntentFilter進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
Android開發(fā)實(shí)現(xiàn)的獲取sdcard大小及內(nèi)存大小工具類
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)的獲取sdcard大小及內(nèi)存大小工具類,涉及Android針對手機(jī)硬件SD卡及內(nèi)存相關(guān)操作技巧,需要的朋友可以參考下2017-11-11
Android開發(fā)中使用sqlite實(shí)現(xiàn)新聞收藏和取消收藏的功能
本篇文章主要介紹了sqlite實(shí)現(xiàn)新聞收藏和取消收藏功能,主要涉及到oracle數(shù)據(jù)庫方面的內(nèi)容,對于Android開發(fā)sqlite實(shí)現(xiàn)收藏和取消功能感興趣的朋友可以參考下本文2016-11-11
android studio3.0.1無法啟動Gradle守護(hù)進(jìn)程的解決方法
這篇文章主要為大家詳細(xì)介紹了android studio3.0.1無法啟動Gradle守護(hù)進(jìn)程的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08
Android為TextView添加字體庫和設(shè)置描邊的方法
本篇文章主要介紹了Android為TextView添加字體庫和設(shè)置描邊的方法,具有一定的參考價(jià)值,有興趣的可以了解一下2017-09-09
Android WebView開發(fā)之WebView與Native交互
隨著H5的廣泛使用,Android開發(fā)過程中免不了會使用網(wǎng)頁來做展示,那么web與native之間的通信就顯得尤其重要了,其實(shí)際上是JavaScript與java之間的通信。本文將為大家詳細(xì)介紹二者是如何實(shí)現(xiàn)交互的,需要的朋友可以參考一下2021-12-12
Android實(shí)現(xiàn)兩個(gè)ScrollView互相聯(lián)動的同步滾動效果代碼
這篇文章主要介紹了Android實(shí)現(xiàn)兩個(gè)ScrollView互相聯(lián)動的同步滾動效果代碼,涉及Android操作ScrollView實(shí)現(xiàn)聯(lián)動功能的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10

