关于webview加载https的正确使用方式
头几天同事遇到webview加载https出现空白页面的情况,简单查了一下发现是因为所使用的证书没有本地授信,同样的网址复制到浏览器中会弹出一个对话框提示查看或者继续,而webview中却没有,网上的方式就是在webviewclient中重写onReceivedSslError这个方法,然后调用handler.proceed();授信就可以,不过这存在一个问题就是没有对加载的https证书进行校验,就直接通过了,容易受到攻击或者代理拦截之类的,所有加上了一层证书的校验,具体的方式看代码:
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
SslCertificate certificate = error.getCertificate();
String cName = certificate.getIssuedBy().getCName();
String uName = certificate.getIssuedTo().getOName();
if(HttpsUtils.checkSslCertificate(certificate)&&
cName.equals(getResources().getString(R.string.ssl_issueby_name))&&
uName.equals(getResources().getString(R.string.company_name))){
handler.proceed();
}
}
原理就是通过SslError取到https的证书,然后分别取使用者和颁发者的名字,进行对比,同时也会对比这个证书与本地的一个证书的职位是否相同。通过方法HttpsUtils.checkSslCertificate():
public static boolean checkSslCertificate(SslCertificate certificate) {
Bundle bundle = SslCertificate.saveState(certificate);
byte[] bytes = bundle.getByteArray("x509-certificate");
if (bytes != null) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Certificate ce = certificateFactory.generateCertificate(new ByteArrayInputStream(bytes));
MessageDigest instance = MessageDigest.getInstance("SHA-256");
byte[] digest = instance.digest(ce.getEncoded());
byte[] local = instance.digest(localCertificate.getEncoded());
return Arrays.equals(local, digest);
} catch (Exception e) {
}
}
return false;
}
其中的localCertificate就是通过读取本地文件而获得的一个证书对象,对比两个证书的指纹是否相同。HttpsUtils这个类是使用Okhttp进行请求时,初始化sslfactory和trustmanager这两个对象的,在获取TrustManager时会读取本地文件生成一个证书,赋值就可以了:
一共有三重校验,证书指纹,颁发者和使用者,基本可以保证安全。
网友评论