您好,登錄后才能下訂單哦!
有些時(shí)候由于Android系統(tǒng)的bug或者其他的原因,導(dǎo)致我們的webview不能驗(yàn)證通過(guò)我們的https證書(shū),最明顯的例子就是華為手機(jī)mate7升級(jí)到Android7.0后,手機(jī)有些網(wǎng)站打不開(kāi)了,而更新了webview的補(bǔ)丁后就沒(méi)問(wèn)題了,充分說(shuō)明系統(tǒng)的bug對(duì)我們混合開(kāi)發(fā)webview加載https地址的影響是巨大的。那么我們?cè)趺慈ソ鉀Q這個(gè)問(wèn)題呢?
首先我們?nèi)シ治鲆幌鲁霈F(xiàn)的原因
當(dāng)webview加載https地址的時(shí)候,如果因?yàn)樽C書(shū)的問(wèn)題出錯(cuò)的時(shí)候就會(huì)走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() 就是讓加載的頁(yè)面白屏,所有導(dǎo)致了如果webview校驗(yàn)證書(shū)存在異常,android在默認(rèn)情況下會(huì)顯示白屏,我們也可調(diào)用handler.proceed(),大多時(shí)候很多人都是這個(gè)處理,但是這也就意味著https證書(shū)失去了他存在的意義了。
那么如果你的網(wǎng)站證書(shū)是正常的,但是因?yàn)橄到y(tǒng)的bug導(dǎo)致了加載異常,這時(shí)候就需要我們手動(dòng)校驗(yàn)了。
其實(shí)我們是可以手動(dòng)校驗(yàn)網(wǎng)站證書(shū)的sha256,如果異常之后校驗(yàn)sha256就執(zhí)行handler.proceed(),失敗就退出應(yīng)用。
首先我們要獲取網(wǎng)站的證書(shū)
利用谷歌瀏覽器,打開(kāi)網(wǎng)址并且按下“F12”,打開(kāi)開(kāi)發(fā)者模式
一步一步導(dǎo)出證書(shū)
然后在打開(kāi)sha256校驗(yàn)網(wǎng)址:http://www.atool.org/file_hash.php
或http://tools.jb51.net/password/sha_encode
這樣就獲取到了證書(shū)的sha256的值,寫(xiě)了一個(gè)工具類(lèi)
/** * SSL證書(shū)錯(cuò)誤,手動(dòng)校驗(yàn)https證書(shū) * * @param cert https證書(shū) * @param sha256Str sha256值 * @return true通過(guò),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è)字符對(duì)應(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) { // 如果手動(dòng)校驗(yàn)sha256成功就允許加載頁(yè)面 if (SSLCertUtil.isSSLCertOk(error.getCertificate(), "6683c9584b8287ec3a50e312f4a540c79938aaeb76bd02e40a9ca037ee5d24f4")) { handler.proceed(); } else { try { new AlertDialog.Builder(MainActivity.this) .setTitle("警告") .setMessage("證書(shū)校驗(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(); } } });
這里我們只是真對(duì)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;
這樣就完成了手動(dòng)校驗(yàn)https證書(shū)校
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。