Https 中用到的证书术语,大概关系
CA机构:颁发证书的机构,你的域名想上Https,需要在CA机构注册,申请证书,如百度https://www.baidu.com/的证书
服务器证书:如https://www.baidu.com/,对应一家公司,去CA申请证书后,会有一个证书(服务器程序会用到),下文中将此类证书称为服务器证书
根证书:CA机构用来加解密,验证你申请证书的证书,每个平台会内置这些CA机构的证书(也叫根证书)如 Android,程序与https通信的时候会用到根证书验证Https URL对应的证书是否合法
查看一个域名的服务器证书和CA根证书
如:https://www.baidu.com/域名
在谷歌浏览器中打开https://www.baidu.com/可以看到(如下图),点击安全图标出现如下弹框
点击证书可以查看证书相关内容:颁发者,有效期等
导出服务器证书 : 点击详细信息->复制到文件->下一步->选择格式DER ->下一步,直到导出成功 最后会有一个**.cer**的证书文件
导出CA根证书 :点击根证书路径->点击最上面的根证书->剩下步骤就和导出服务器证书一样
window上证书查看
window+R 下输入certmgr.msc,确定
出现证书列表:选择某个证书,双击出现证书界面,导出就和上面的操作一样
查看Android 设备上的证书,安装证书到Android设备
查看设备上的证书
adb shell
cd system/etc/security/cacerts/
#列出所有证书,如下图 ,一堆57692373.0文件
ls
#查找某个机构颁发的证书 如 cfca
grep -inr "cfca" ./
#查看某个证书文件
cat 9282e51c.0
列出所有证书
查找某个机构颁发的证书 如 cfca,查看cfca证书文件
安装证书
- 把之前的.cer根证书文件push到android设备上
adb push "C:\Users\my\Desktop\CFCA EV ROOT.cer" /sdcard/baidu
- 在设置中安装证书
进入设置->安全->从手机存储安装(从手机存储安装证书),不同设备可能位置不一样;选择刚才push的证书文件
note:如果是ODM公司,rom开发可以把根证书内置到系统中
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
上面的错误是在一个Android 5.0的设备的Https请求中的,其他设备没问题;通过上面的步骤安装完证书就好了,然而这种方法只能验证问题,不能当做解决问题的方案。下面提供两个解决思路
- 服务器提供的https接口,注册的CA机构根证书不是很通用,在某些设备上没有,那就需要服务器更换证书,换一个更通用的CA机构,思路没毛病,服务器的问题。
- 终端处理:
- 忽略https根证书验证,设置信任所有服务器证书(有点违背使用https的原则,不安全)
- 终端内置服务器证书,修改证书验证逻辑,与内置的服务器证书匹配(证书公钥,颁发机构,颁发给谁等信息)就算验证通过
- 终端内置根证书,代码安装根证书
OkHttp的https证书验证逻辑
使用OkHttp默认证书验证逻辑:不设置sslSocketFactory和hostnameVerifier,默认使用系统的根证书验证服务器证书,如果对应的根证书不存在,报错Trust anchor for certification path not found.
;这种就适合使用CA的证书(也有可能CA机构的根证书不够通用而没有内置),不适合像12306这种自己颁发的证书
自定义自己的证书验证逻辑:直接让所有的服务器证书通过不验证;比较服务器证书公钥,颁发机构,颁发给谁等信息验证某个特定的服务器证书
相关代码
OkHttp默认证书验证逻辑
private X509TrustManager systemDefaultTrustManager() {
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
} catch (GeneralSecurityException e) {
throw assertionError("No System TLS", e); // The system has no TLS. Just give up.
}
}
private SSLSocketFactory systemDefaultSslSocketFactory(X509TrustManager trustManager) {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] {
trustManager }, null);
return sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
throw assertionError("No System TLS", e); // The system has no TLS. Just give up.</