一、布景简介
1、遇到的问题
2020年,货拉拉运营部分和客户端开发对齐了https网络通信协议中的SSL网络证书校验计划;可是因为Android客户端的证书装备不标准,导致在客户端内置的SSL网络证书到期前十几天被发现证书校验反常,Android客户端面临全网拜访反常的问题
2、本文内容
本文首要介绍处理货拉拉Android客户端SSL证书到期的处理计划及Android端SSL证书相关常识
二、SSL证书简介
1、SSL证书诞生布景
1994年,Netscape公司首先运用了SSL协议,SSL协议全称为:安全套接层协议(Secure Sockets Layer),它指定了在应用程序协议(如HTTP、Telnet、FTP)和TCP/IP之间供给数据安全性分层的机制,它是在传输通信协议(TCP/IP)上完成的一种安全协议,采用公开密钥技能,它为TCP/IP连接供给数据加密、服务器认证、消息完整性以及可选的客户端认证。因为SSL协议很好地处理了互联网明文传输的不安全问题,很快得到了业界的支撑,并现已成为国际标准
HyperText Transfer Protocol over Secure Socket Layer。在HTTPS中,运用传输层安全性(TLS)或安全套接字层(SSL)对通信协议进行加密。也便是HTTP+SSL(TLS)=HTTPS
2、SSL证书简介
按类型区分,SSL证书包括CA证书、用户证书两种
(1)CA证书(Certification Authority证书颁布安排)
证书的签发安排(CA)颁布的电子证书,包括根证书和中心证书两种
[i]根证书
归于根证书颁布安排(CA)的公钥证书,是在公开密钥根底建设中,信赖链的起点
一般客户端会内置
[ii]中心证书
因为根证书太宝贵了,直接颁布危险太大了。因而,为了保护根证书,CAs通常会颁布所谓的中心证书。CA运用它的私钥对中心证书签名,使它遭到信赖。然后CA运用中心证书的私钥签署和颁布终端用户SSL证书。这个进程能够执行屡次,其间一个中心根对另一个中心根进行签名
(2)用户证书
用户证书是由CA中心证书签发给用户的证书,包括服务器证书、客户端证书
[i]服务器证书
组成Web服务器的SSL安全功用的仅有的数字标识。 经过CA签发,并为用户供给验证您Web站点身份的手法。
服务器证书包括具体的身份验证信息,如服务器内容附属的安排、颁布证书的安排以及称为公开密钥的仅有的身份验证文件
[ii]客户端证书
在双向https验证中,就必须有客户端证书,生成办法同服务器证书相同;
单向证书则不必生成
3、SSL证书链
SSL证书链是从用户证书、生成用户证书的CA中心证书、生成CA中心证书的CA中心证书…一直到CA根证书;其间根证书只能有一个,可是CA中心证书能够有多个
(1)以baidu的证书为例
(2)证书链
客户端(比如浏览器或许Android手机)验证咱们SSL证书的有效性的时分,会一层层的去寻找颁布者的证书,直到自签名的根证书,然后经过相应的公钥再反过来验证下一级的数字签名的正确性
任何数字证书都必须要有根证书做支撑,有了根证书的支撑才说明这个数字证书是有效的是被信赖的
4、SSL证书文件的后缀
证书的后缀首要有.key、.csr、.crt、.pem等
(1).key文件:密钥文件,SSL证书的私钥就包括在其间
(2).csr文件:这个文件里边包括着证书的公钥和其他一些公司信息,经过恳求签名之后就能够直接生出证书
(3).crt文件:该文件中也包括了证书的公钥、签名信息以及根据不同类型证书携带不同的认证信息,如IP等(该文件在有些安排、体系中也或许表现为.cert后缀)
(4).pem文件:该文件相比照较少见,里边包括着证书的私钥以及部分证书信息
5、SSL用户证书类型
SSL用户证书首要分为(1)DV SSL证书 (2)OV SSL证书 (3)EV SSL证书
(1)DV SSL证书(域名验证型):只需验证域名所有权,无需人工验证恳求单位实在身份,几分钟就可颁布的SSL证书。价格一般在百元至千元左右,适用于个人或许小型网站
(2)OV SSL证书(企业验证型):需求验证域名所有权以及企业身份信息,证明恳求单位是一个合法存在的实在实体,一般在1~5个工作日颁布。价格一般在百元至几千元左右,适用于企业型用户恳求
(3)EV SSL证书(扩展验证型):除了需求验证域名所有权以及企业身份信息之外,还需求提交一下扩展型验证,通常CA安排还会进行电话回访,一般在2~7个工作日颁布证书。价格一般在千元至万元左右,适用于在线交易网站、企业型网站
6、SSL证书结构
7、SSL证书检查
以Chorme上的baidu为例:
第1步
第2步
第3步
三、客户端SSL证书校验流程
1、客户端SSL证书校验首要是在网络连接的SSL/TLS握手环节校验
SSL/TLS握手(用非对称加密的手法传递密钥,然后用密钥进行对称加密传递数据)
校验流程首要在上述进程的第三步和第六步
第三步:Certificate
Server——>Client 服务端下发公钥证书
第六步:证书合法性校验
Client 对 Server下发的公钥证书进行合法性校验
2、客户端证书校验进程
(1)校验证书是否是受信赖的CA根证书颁布安排颁布
客户端经过服务器证书 中签发安排信息,获取到中心证书公钥;运用中心证书公钥进行服务器证书的签名验证
a、中心证书公钥解密 服务器签名,得到证书摘要信息;
b、摘要算法计算 服务器证书 摘要信息;
c、然后比照两个摘要信息。
客户端经过中心证书中签发安排信息,客户端本地查找到根证书公钥;运用根证书公钥进行中心证书的签名验证
(2)客户端校验服务端证书公钥及摘要信息
客户端获取到服务端的公钥:Https恳求 TLS握手进程中,服务器公钥会下发到恳求的客户端。
客户端用存储在本地的CA安排的公钥,对服务端公钥中对应的摘要信息进行解密,获取到服务端公钥的摘要信息A;
客户端根据对服务端公钥进行摘要计算,得到摘要信息B;
比照摘要信息A与B,相同则证书验证经过
(3)校验证书是否在上级证书的撤消列表
若证书的恳求主体呈现:私钥丢失、恳求证书无效等状况,CA安排需求抛弃该证书
(具体战略见《四、Android端证书撤消校验战略》)
(4)校验证书是否过期
校验证书的有效期是否现已过期:首要判别证书中**Validity period
**字段是否过期(ps:Android体系默许不校验证书有效期,但浏览器和ios体系默许会校验证书有效期)
(5)校验证书域名是否共同
校验证书域名是否共同:核查
证书域名
是否与当时的拜访域名
匹配
。
比如:咱们恳求的域名 www.huolala.cn 是否与**证书文件
中DNS标签
下所列的域名
相匹配
**;
四、Android端证书撤消校验战略
1、证书撤消校验首要存在两类机制:CRL 与 OCSP
(1)证书撤消列表校验:CRL(Certificate Revocation List)
证书撤消列表:是一个单独的文件,该文件包括了 CA安排 现已撤消的证书序列号与撤消日期;
证书中一般会包括一个 URL 地址 CRL Distribution Point,通知运用者去哪里下载对应的 CRL 以校验证书是否撤消。
该撤消办法的长处是不需求频繁更新,可是不能及时撤消证书,这期间或许现已造成了极大丢失
(2)证书状况在线查询:OCSP(Online Certificate Status Protocol)
证书状况在线查询协议:一个实时查询证书是否撤消的办法。
恳求者发送证书的信息并恳求查询,服务器回来正常、撤消或未知中的任何一个状况。
证书中一般也会包括一个 OCSP 的 URL 地址,要求查询服务器具有杰出的功用。
部分 CA 或大部分的自签 CA (根证书)都是未供给 CRL 或 OCSP 地址的,对于撤消证书会是一件非常麻烦的事情
2、Android体系默许运用CRL办法来校验证书是否被撤消
中心完成类是CertBlocklistImpl(保护了本地黑名单列表),部分源码逻辑如下:
(1)TrustManagerImpl(证书校验中心类)
第1步循环校验信赖证书
第2步检查该证书是否在黑名单列表里边
(2)CertBlocklistImpl(证书黑名单列表保护类)
黑名单校验逻辑:首要检查是否在黑名单列表里边
黑名单本地存储方位
能够看到黑名单文件储存在环境变量“ANDROID_DATA”/misc/keychain/pubkey_blacklist.txt;
能够经过adb shell–export–echo $ANDROID_DATA,拿到环境变量方位,一般在/data目录下
3、Android端自界说证书撤消校验逻辑
中心类在TrustManagerFactory、CertPathTrustManagerParameters、PKIXRevocationChecker
(1)TrustManagerFactory工厂模式的证书管理类
有两种init办法
[i]init(KeyStore ks) 默许运用
传递私钥,一般传递体系默许或许传空
以okhttp为例(默许传空)
[ii]init(ManagerFactoryParameters spec) 自界说办法
下面介绍下经过自界说办法来完成OCSP办法校验证书是否撤消
4、根据PKIXRevocationChecker办法自界说OCSP办法
(1)自界说TrustManagerFactory.init(ManagerFactoryParameters spec)
init办法传入根据CertPath的TrustManager
CertPathTrustManagerParameters,包装战略PKIXRevocationChecker
(2)PKIXRevocationChecker(用于检查PKIX算法的证书撤消状况)
默许运用OCSP办法校验,能够自界说运用OCSP战略仍是CLR战略
参阅谷歌开发者文档:developers.google.cn/j2objc/java…
五、Android端证书校验办法
首要有四种校验办法:
客户端单向认证服务端—证书确定
客户端单向认证服务端—公钥确定
客户端服务端双向认证
客户端信赖所有证书
1、客户端单向认证服务端—证书确定
(1)校验进程
校验服务端证书的subject信息和publickey信息是否与客户端内置证书共同,假如不共同会报错:
“java.security.cert.CertPathValidatorException: Trust anchor for certification path not found”
(2)完成办法
[i]network-security-config装备办法
(收效规模:app大局,包括webview恳求)
(只支撑android7.0及以上)
[ii]代码装备办法(收效规模:装备了该SSLParams的实例)
(3)长处
校验了subject信息和publickey信息,防信息篡改的安全等级高一点
(4)缺陷
[i]因为一般网络证书的有效期是1-2年,所以面临过期之后或许校验反常的问题(ps:本次货拉拉客户端遇到的便是这种内置的网络证书快到期的case)
[ii]内置在app里边,证书容易走漏
2、客户端单向认证服务端—公钥确定
(1)校验进程
校验服务端证书的公钥信息是否与客户端内置证书的共同
(2)完成办法
[i]network-security-config装备办法
(收效规模:app大局,包括webview恳求)
(只支撑android7.0及以上)
[ii]代码装备办法(收效规模:装备了该参数的实例)
(3)长处
只要服务端的公钥坚持不变,替换证书也能经过校验
(4)缺陷
只校验了公钥,防信息篡改的安全等级低一点
3、客户端和服务端双向认证
(1)完成办法
自界说的SSLSocketFactory完成客户端和服务端双向认证
public class SSLHelper {
/** * 存储客户端自己的密钥 */ private final static String CLIENT_PRI_KEY = "client.bks";
/** * 存储服务器的公钥 */ private final static String TRUSTSTORE_PUB_KEY = "publickey.bks";
/** * 读取暗码 */ private final static String CLIENT_BKS_PASSWORD = "123321";
/** * 读取暗码 */ private final static String PUCBLICKEY_BKS_PASSWORD = "123321";
private final static String KEYSTORE_TYPE = "BKS";
private final static String PROTOCOL_TYPE = "TLS";
private final static String CERTIFICATE_STANDARD = "X509";
public static SSLSocketFactory getSSLCertifcation(Context context) {
SSLSocketFactory sslSocketFactory = null;
try {
// 服务器端需求验证的客户端证书,其实便是客户端的keystore
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
// 客户端信赖的服务器端证书
KeyStore trustStore = KeyStore.getInstance(KEYSTORE_TYPE);
//读取证书
InputStream ksIn = context.getAssets().open(CLIENT_PRI_KEY);
InputStream tsIn = context.getAssets().open(TRUSTSTORE_PUB_KEY);
//加载证书
keyStore.load(ksIn, CLIENT_BKS_PASSWORD.toCharArray());
trustStore.load(tsIn, PUCBLICKEY_BKS_PASSWORD.toCharArray());
//封闭流
ksIn.close();
tsIn.close();
//初始化SSLContext
SSLContext sslContext = SSLContext.getInstance(PROTOCOL_TYPE);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(CERTIFICATE_STANDARD);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(CERTIFICATE_STANDARD);
trustManagerFactory.init(trustStore);
keyManagerFactory.init(keyStore, CLIENT_BKS_PASSWORD.toCharArray());
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
sslSocketFactory = sslContext.getSocketFactory();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
return sslSocketFactory;
}
}
(2)长处
双向校验更安全
(3)缺陷
需求服务端支撑,TLS/SSL握手耗时增加
4、客户端信赖所有证书
不查验任何证书,下面列两种常见的完成办法
(1)OkHttp版别
(2)HttpURLConnection版别
六、Android端一种源码调试的办法
布景:因为证书校验相关源码不在Android.jar中,为了便利调试证书校验的流程,这儿简单介绍一种非android.jar包中的Android源码调试的办法
1、下载源码
(1)源码地址:android.googlesource.com/
android官方供给了各个模块的git库房地址
(2)以SSL证书调试为例
咱们只需求conscrypt部分的源码:android.googlesource.com/platform/ex…
留意点:挑选的分支要和被调试的手机版别共同(因为不同体系版别下源码有点区别)
假如测验及时Android10.0体系,咱们能够挑选android10-release分支
2、源码导入
新建一个module 把刚才的体系源码仿制进来,不需求依赖,只需求在setting.gradle中include,这样做阻隔性好,便利移除
3、源码编译
导入源码之后,或许会有部分编译问题,能够处理的能够先处理,假如处理不了能够先注释;
需求留意点:
(1)不能修改行号,不然调试的时分走不到
(2)不能新增代码,新增的代码不会执行
4、断点调试
打好断点就能够发车了
能够看到app建议网络恳求之后会走到TrustManagerImpl里边的checkServerTrusted校验服务端证书
七、Android端证书校验源码解析
1、证书校验首要分3步
(1)握手进程中验证证书
验证证书合法性,判别是否由合法的CA签发,由上面的Android体系根证书库来判别
(2)验证域名
判别服务端证书是否为特定域名签发,验证网站身份,这儿假如犯错就会抛出
SSLPeerUnverifiedException
的反常
(3)验证证书绑定
2、Android根证书相关源码
Android会内置常用的根证书,体系根证书存放在/system/etc/security/cacerts 目录下,文件均为 PEM 格局的 X509 证书格局,包括明文base64编码公钥,证书信息,哈希等
Android体系的根证书管理类
坐落/frameworks/base/core/java/android/security/net/config
目录下
以下是根证书管理类的类联系图
(1)CertificateSource
接口类,界说了对根证书可执行的获取和查询操作
有三个完成类,分别是KeyStoreCertificateSource、ResourceCertificateSource、DirectoryCertificateSource
(2)KeyStoreCertificateSource
从 KeyStore 中获取证书
(3)ResourceCertificateSource
根据 ResourceId 从资源目录读取文件并构造证书
(4)DirectoryCertificateSource(笼统类)
遍历指定的目录 mDir 读取证书;还供给了一个笼统办法 isCertMarkedAsRemoved()
用于判别证书是否被移除
SystemCertificateSource
和 UserCertificateSource
承继了DirectoryCertificateSource而且分别界说了体系和用户根证书库的途径,并完成笼统办法
[i]SystemCertificateSource
界说了体系证书查询途径,而且还指定了被移除的证书文件的目录
判别证书是否移除便是直接判别证书文件是否存在于指定的目录
[ii]UserCertificateSource
界说了用户证书指定查询途径,证书是否移除永远为false
3、Android证书校验源码
(以证书确定办法的单向校验服务端证书为例)
中心类TrustManagerImpl、TrustedCertificateIndex、X500Principal
(1)第一步checkServerTrusted()
(2)第二步checkTrusted()
(3)第三步TrustedCertificateIndex类匹配证书issuer和signature信息
private final Map<X500Principal, List> subjectToTrustAnchors
= new HashMap<X500Principal, List>();
能够看到获取TrustAnchor是经过HashMap的key X500Principal匹配获取的,
(4)X500Principal
private transient X500Name thisX500Name;
检查X500Principal的源码能够看到它覆写了equals()办法,比照的是特点中的thisX500Name
调试下来发现咱们客户端证书的 thisX500Name 的值为
“CN=*. huolala.cn , OU=IT, O=深圳货拉拉科技有限公司, L=深圳市, ST=广东省, C=CN”
(ps:后面会说到,货拉拉客户端证书反常首要因为新证书短少了OU字段)
(5)subject和issue信息
八、货拉拉SSL证书踩坑流程
1、布景简介
2020年7月份的时分,货拉拉呈现了因为网络证书过期导致的反常,所以运维的搭档拉了客户端的搭档一同对齐了计划,运用上述《客户端单向认证服务端—公钥确定》的办法
因为历史原因:
货拉拉用户端运用了上述(三、1(2)客户端单向认证服务端—证书确定,代码装备办法)
货拉拉司机端运用了上述(三、1(1)客户端单向认证服务端—证书确定,network-security-config装备办法)
2021年7月份的时分,运维搭档更新了服务端的证书,因为替换进程中没有呈现反常,所以运维的搭档以为android端都是依照之前约定的《客户端单向认证服务端—公钥确定》办法
(但实践原因是用户和司机端提前内置了2022-8-19过期的证书)
2、线上呈现反常
2022-8-1的时分,运维搭档开端操作更新服务端2023年的证书,在更新了H5部分域名的证书之后,司机Android端呈现部分网页白屏的问题
排查之后发现服务端更新了证书导致客户端证书校验证书非法导致反常
2022-8-2的时分开端排查用户端的逻辑,发现是《客户端单向认证服务端—证书确定,代码装备办法》,测验之后发现
(1)删去app内置2022年的证书,只保留2020年的证书之后,native恳求反常,无法进入app
(2)手动调整手机设备时间,发现native恳求正常,webview白屏和图片加载失利
意味着在服务端替换的证书2022-8-19到期之后,客户端将面临全网拜访反常的问题
3、第一次测验处理
测验的时分发现,android端在证书过期时仍然能够拜访服务端(客户端和服务端都坚持共同的2022年的证书);
所以想的第1个处理计划是服务端仍然运用2022-8-19的证书,直到大部分用户升级上来之后再替换新证书;
可是ios和web发现假如服务端运用过期证书的状况,体系底层会拦截这个过期证书直接报错;
所以无法兼容所有客户端
4、第2次测验处理
在检查源码TrustManagerImpl类源码的时分发现,TrustManagerImpl的服务端查验仅仅校验了publickey(公钥),所以假如2022年的旧证书和2023年的新证书假如公钥共同的话,或许能够校验经过;
所以想的第2个处理计划是服务端运用的新证书坚持和2022-8-19的证书的公钥共同就能够;
可是测验的时分发现native恳求仍是会报错
“java.security.cert.CertPathValidatorException: Trust anchor for certification path not found”
5、第三次测验处理
开发发现依照证书链的校验进程,如下:
假如有中心证书,那么这个中心证书安排颁布的任何服务器证书都能够都校验经过;
所以想出的第3个处理计划是服务器证书内置中心证书组成证书链;
可是排查之后发现服务器证书和客户端内置的证书里边都现已包括了中心证书,所以依然行不通
(ps:假如客户端内置的证书里边删去用户证书信息,只保留中心证书信息,那么只要是这家中心证书颁布的所有的服务器证书都是能够校验经过的,而且一般中心证书的有效期是10年,这也能够作为一个备选项,不过缺陷是不安全)
6、第四次测验处理
(1)测验同学在网上找到一篇《那些年踩过HTTPS的坑(二)——APP证书链mp.weixin.qq.com/s/yv_XcMLvr…
所以想到的处理计划是重新恳求一个带OU字段的新服务器证书
(2)可是运维搭档咨询了两家之前的中心商之后对方的回复都是新的证书现已不再供给OU字段,理由是
(3)最终历经一言难尽的各种插曲最终找UniTrust颁布了带OU字段的新证书
(ps:还在运用证书确定办法校验的能够留意下证书里边的OU字段,后续证书都不会再供给)
九、Android端证书校验的处理计划
1、认证办法
依照安全等级区分,从高到低依次为:
(1)客户端和服务端双向认证,参阅上述《五、Android端证书校验办法-3、客户端和服务端双向认证》
(2)客户端单向认证服务端—证书确定,参阅上述《五、Android端证书校验办法-1、客户端单向认证服务端—证书确定》
(3)客户端单向认证服务端—公钥确定,参阅上述《五、Android端证书校验办法-2、客户端单向认证服务端—公钥确定》
能够根据各自的安全需求挑选适宜的认证办法
2、校验办法
(1)证书校验
具体办法参阅《五、Android端证书校验办法-1、客户端单向认证服务端—证书确定》;
为了增强安全性,app能够内置加密后的证书,将解密信息存放在加固后的c++端,增强安全性
(2)公钥校验
具体办法参阅《五、Android端证书校验办法-2、客户端单向认证服务端—公钥确定》;
为了增强安全性,app能够内置加密后的公钥,将解密信息存放在加固后的c++端,增强安全性
3、装备降级
为了在呈现反常状况时不影响app拜访,能够增加动态装备和动态降级才能
(1)动态装备
动态下发公钥和证书信息,需求留意下发的时机要尽量早一点,避免证书反常时走不到下发的恳求
(2)动态降级
动态降级证书校验功用,在客户端证书或许服务端证书呈现反常时,支撑动态封闭所有的证书校验的功用
十、总结
最终,总结一下全体的思路:
1、SSL证书分为CA证书和用户证书
2、客户端SSL证书校验是在网络连接的SSL/TLS握手环节进行校验
3、SSL证书的认证办法分为(1)单向认证(2)双向认证
4、SSL证书的校验办法分为(1)证书校验(2)公钥校验
5、SSL证书的校验流程首要是校验证书是否是由受信赖的CA安排签发的合法证书
6、SSL证书的撤消校验战略分为(1)CRL本地校验证书撤消列表(2)OCSP证书状况在线查询
7、纵观本次踩坑之旅,也暴露出一个比较深入的问题:大部分的客户端开发的认知仍是停留在app上层,短少对底层技能的知道和探究,导致一个很小的装备问题差点酿成大的事端;这也为想在客户端范畴进一步提升供给了一个思路:多学习客户端的底层技能,包括网络底层完成、安全、体系底层源码等等
8、最终,处理技能类问题最中心的点仍是学习和了解源代码;处理证书装备问题的进程中,走了不少弯路,本质上是最开端没有完全掌握证书校验相关的体系源代码的逻辑,客观上是因为短少非android.jar源码的调试手法导致阅览源码遗漏了部分校验逻辑,所以本次特意补上(六、Android端一种源码调试的办法),希望后续遇到体系级的疑难杂症能够用的上
参阅:
www.cnblogs.com/xiaxveliang…
blog.csdn.net/weixin_3501…