欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android webview手动校验https证书(by 星空武哥)

程序员文章站 2023-12-18 17:46:28
有些时候由于android系统的bug或者其他的原因,导致我们的webview不能验证通过我们的https证书,最明显的例子就是华为手机mate7升级到android7.0...

有些时候由于android系统的bug或者其他的原因,导致我们的webview不能验证通过我们的https证书,最明显的例子就是华为手机mate7升级到android7.0后,手机有些网站打不开了,而更新了webview的补丁后就没问题了,充分说明系统的bug对我们混合开发webview加载https地址的影响是巨大的。那么我们怎么去解决这个问题呢?

Android webview手动校验https证书(by 星空武哥)

首先我们去分析一下出现的原因
当webview加载https地址的时候,如果因为证书的问题出错的时候就会走onreceivedsslerror()方法

webview.setwebviewclient(new webviewclient() { 
 
  @override 
  public void onreceivedsslerror(webview view, sslerrorhandler handler, sslerror error) { 
    super.onreceivedsslerror(view, handler, error); 
  } 
} 

而super.onreceivedsslerror()默认是

Android webview手动校验https证书(by 星空武哥)

handler.cancel() 就是让加载的页面白屏,所有导致了如果webview校验证书存在异常,android在默认情况下会显示白屏,我们也可调用handler.proceed(),大多时候很多人都是这个处理,但是这也就意味着https证书失去了他存在的意义了。

Android webview手动校验https证书(by 星空武哥)

那么如果你的网站证书是正常的,但是因为系统的bug导致了加载异常,这时候就需要我们手动校验了。
其实我们是可以手动校验网站证书的sha256,如果异常之后校验sha256就执行handler.proceed(),失败就退出应用。
首先我们要获取网站的证书
利用谷歌浏览器,打开网址并且按下“f12”,打开开发者模式

Android webview手动校验https证书(by 星空武哥)

一步一步导出证书

Android webview手动校验https证书(by 星空武哥)

然后在打开sha256校验网址:http://www.atool.org/file_hash.php

Android webview手动校验https证书(by 星空武哥)

这样就获取到了证书的sha256的值,写了一个工具类

  /**
   * ssl证书错误,手动校验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转bytearr
   * <p>例如:</p>
   * hexstring2bytes("00a8") returns { 0, (byte) 0xa8 }
   *
   * @param hexstring
   * @return 字节数组
   */
  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; // 两个字符对应一个byte
      int h = hexdigits.indexof(hexchars[pos]) << 4; // 注1
      int l = hexdigits.indexof(hexchars[pos + 1]); // 注2
      if (h == -1 || l == -1) { // 非16进制字符
        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) {
			// 如果手动校验sha256成功就允许加载页面
			if (sslcertutil.issslcertok(error.getcertificate(), "6683c9584b8287ec3a50e312f4a540c79938aaeb76bd02e40a9ca037ee5d24f4")) {
				handler.proceed();
			} else {
				try {
					new alertdialog.builder(mainactivity.this)
							.settitle("警告")
							.setmessage("证书校验失败")
							.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进行了判断,可能还有其他情况,根据自己的情况判定。

/**
 * 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;

这样就完成了手动校验https证书校

上一篇:

下一篇: