在okhttp源码解析中详细的分析了下其内部原理,现在就okhttp配置https的东东做一个简单的笔记。
网上查询的一些Okhttp中忽略HTTPS验证的代码如下所示。
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain,
String authType) throws CertificateException {
//该方法在服务端使用,用来校验客户端的安全性
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain,
String authType) throws CertificateException {
//该方法在客户端使用,用来校验服务端的安全性
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}};
忽略验证这种做法肯定不可取,对于客户端APP来说,在TrustManager中有一个方法需要我们关注,就是checkServerTrusted。顾名思义,就是用来验证服务器是否可靠。其中第一个参数是证书链,往往数组中第一个X509Certificate对象就是我们APP服务器的证书对象。通过该对象,我们可以获取证书的序列号,公钥等各种信息。
默认如果什么都不写的话,就是默认所有服务器都可靠。直接一路绿灯,畅通无阻。
注意该方法抛出一个异常CertificateException,如果自己实现这个方法,并且抛出这个异常的话,那么就说明服务器认证不通过,此时https请求返回的状态码会报600错误。比如X509Certificate对象的checkValidity方法就会抛出CertificateException的两个之类异常:一个是证书过期,一个是证书失效。
public abstract void checkValidity(Date date)
throws CertificateExpiredException, CertificateNotYetValidException;
所以我们如果简单的将checkServerTrusted如下写法的话,如果证书校验失败,HTTPS请求就会访问不通。比如我将手机系统时间往后设置30年,此时证书已经过期,运行后就会抛出CertificateExpiredException异常:
public void checkServerTrusted(java.security.cert.X509Certificate[] chain,
String authType) throws CertificateException {
//服务器验失败会抛异常
chain[0].checkValidity();
}
如果我们证书校验失败,我们想要给用户提示的话,就可以先捕获了CertificateException异常,在catch里面处理:
public void checkServerTrusted(java.security.cert.X509Certificate[] chain,
String authType) throws CertificateException {
try{
chain[0].checkValidity();
}catch(CertificateExpiredException e1){
//给用户弹出安全性提示
}catch(CertificateNotYetValidException e2){
//给用户弹出安全性提示
}
}
其实可以这么做,这部分可以参考Android App 安全的HTTPS 通信: 1、打包一份到证书到 app 内部,通过CertificateFactory 加载证书输入流,获取Certificate对象 2、自定义一个TrustManager,根据服务端传过来的证书链如步骤1生成的Certificate做对比,自己实现校验逻辑,代码如下:
try {
//获取本地证书
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(getAssets().open("certName.crt"));
final Certificate ca;
try {
ca = cf.generateCertificate(caInput);
} finally {
caInput.close();
}
SSLContext context = SSLContext.getInstance("TLSv1","AndroidOpenSSL");
context.init(null, new TrustManager[]{
new X509TrustManager() {
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
for (X509Certificate cert : chain) {
cert.checkValidity();
//省略try catch
cert.verify(((X509Certificate) ca).getPublicKey());
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
}, null);
关于TrustManager暂且写这么多。其具体的实现逻辑可以移步博主的Android RootTrustManager 证书校验简单分析了解更多。