看一遍忘一遍,还是自己写一遍,看看这次能记多久。
在TLS握手过程中,通过证书校验认证服务端的身份和交换加密秘钥,握手完成之后后续就可以进行加密数据传输。
在浏览器地址栏上点击锁的图标,能打开查看证书的详细信息。
首先可以看到证书包含了多个层级,最下层的是我们直接访问的网站的服务证书,最上层是根证书。每个证书都属于一个机构组织。除了根证书是由根CA自己签发给自己的,其他证书都是由上一级组织用上一级的证书签发出来的,所以形成了层级结构。
点开细节可以看到证书的更多信息,主要看证书包含什么信息?
- 签发机构,证书是哪个机构签发的
- 持有机构,使用证书的机构
- 证书有效期
- 证书持有者公钥,用于解密下一级证书或者用于传输加密
- 指纹哈希算法,对证书内容计算哈希,生成指纹
- 证书指纹,对证书内容做哈希,用于验证证书没被修改过
- 证书签名,使用签发机构的私钥对指纹加密生成签名,非对称加密用于安全认证
- 证书签名算法
假设证书链是根CA颁发证书给中间CA,中间CA给网站颁发服务证书,验证过程是怎么样的?
- 客户端访问网站,收到服务证书
- 服务证书的颁发机构是中间CA,在验证服务证书前要验证中间CA,于是先验证中间CA的证书
- 中间CA的证书由根CA颁发,根CA的根证书内置在浏览器或操作系统中,所以根证书不用验证,可以直接信任
- 用根证书中的公钥解密中间CA证书的签名,拿到中间CA证书里的指纹,和计算出的指纹对比,一致则说明中间CA证书可信
- 用中间CA证书中的公钥解密服务证书的签名拿到指纹,和直接计算的指纹对比,一致则服务证书可信
- 服务证书可信,则证书上的信息是对的,接着检查持有机构是不是要访问的站点,证书有没有过期等
- 至此,验证了访问的站点服务是可信的,继续后续TLS握手过程
说起来是不是很抽象,使用baidu证书走一遍验证的流程。这里我执行命令的系统是Ubuntu。
1. 获取证书文件
可以看到返回了三级证书,分别对应depth从0到2
openssl s_client -showcerts -connect www.baidu.com:443 </dev/null > baidu.pem
depth=2 OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
verify return:1
depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign RSA OV SSL CA 2018
verify return:1
depth=0 C = CN, ST = beijing, L = beijing, O = "Beijing Baidu Netcom Science Technology Co., Ltd", CN = baidu.com
verify return:1
DONE
打开baidu.pem看看,里面清楚地描述了证书链。注意看CN的字段。
0 是baidu.com的证书,由GlobalSign RSA OV SSL CA 2018颁发;
1 是GlobalSign RSA OV SSL CA 2018的证书,由GlobalSign颁发;
2 是GlobalSign的证书,由GlobalSign Root CA颁发;
Certificate chain
# s表示证书持有人,i表示证书颁发机构
0 s:C = CN, ST = beijing, L = beijing, O = "Beijing Baidu Netcom Science Technology Co., Ltd", CN = baidu.com
i:C = BE, O = GlobalSign nv-sa, CN = GlobalSign RSA OV SSL CA 2018
-----BEGIN CERTIFICATE-----
MIIJ6DCCCNCgAwIBAgIMVeasrtH4pDD5qTjFMA0GCSqGSIb3DQEBCwUAMFAxCzAJ
.........
-----END CERTIFICATE-----
1 s:C = BE, O = GlobalSign nv-sa, CN = GlobalSign RSA OV SSL CA 2018
i:OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
-----BEGIN CERTIFICATE-----
MIIETjCCAzagAwIBAgINAe5fIh38YjvUMzqFVzANBgkqhkiG9w0BAQsFADBMMSAw
..............
-----END CERTIFICATE-----
2 s:OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
i:C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
-----BEGIN CERTIFICATE-----
MIIETjCCAzagAwIBAgINAe5fFp3/lzUrZGXWajANBgkqhkiG9w0BAQsFADBXMQsw
....................
-----END CERTIFICATE-----
Server certificate
subject=C = CN, ST = beijing, L = beijing, O = "Beijing Baidu Netcom Science Technology Co., Ltd", CN = baidu.com
issuer=C = BE, O = GlobalSign nv-sa, CN = GlobalSign RSA OV SSL CA 2018
---
注意到,浏览器上的根证书只到GlobalSign,也就是GlobalSign自己给自己颁发的。但是上面返回的内容并没有一个自己给自己颁发的根证书,和在浏览器上看到的不太一样。命令程序直接访问的证书验证是由操作系统完成的,而浏览器可能使用自己内置的根证书,两者返回的证书链可能不完全一致。
2. 提取三个证书
分别提取三个证书,保存为三个文件,为方便区别重名下。
cat baidu.pem | awk '/BEGIN/,/END/{if(/BEGIN/){++i}; out="cert"i".crt"; print >out}'
mv cert1.crt baidu.crt
mv cert2.crt inter-nv.crt
mv cert3.crt inter-r3.crt
3. 校验证书
inter-r3.crt 由GlobalSign_Root_CA签发的,使用系统内置的根证书校验
inter-nv.crt 由GlobalSign Root CA - R3签发,使用inter-r3.crt校验
baidu.crt 由GlobalSign nv-sa签发,使用inter-nv.crt校验
openssl verify -CAfile /etc/ssl/certs/GlobalSign_Root_CA.pem -no-CApath -untrusted inter-r3.crt -untrusted inter-nv.crt baidu.crt
baidu.crt: OK
我们可以手动执行下最后一步baidu.com证书验证的步骤。
4. 查看签名
openssl x509 -in baidu.crt -text -noout -certopt ca_default -certopt no_validity -certopt no_serial -certopt no_subject -certopt no_extensions -certopt no_signame
Signature Algorithm: sha256WithRSAEncryption
19:5a:67:50:43:b1:ac:7a:93:a8:68:18:72:8b:40:7e:a6:75:
de:ac:21:fc:c9:41:16:20:4b:f3:8c:0b:b9:47:45:ae:f8:5d:
79:f6:43:35:26:01:98:f0:b9:86:3e:29:01:f1:df:b0:72:b5:
ae:78:d2:df:61:b6:78:67:8a:c9:77:9a:de:e0:e4:41:2f:9c:
1e:e5:3b:7c:97:3f:42:2f:ad:e3:49:7f:9d:2b:02:88:90:69:
25:03:01:14:b9:b5:cb:0f:59:3d:2d:97:3d:02:d5:51:90:69:
0c:81:10:22:da:c6:51:ef:48:0c:d2:4f:de:61:f2:6a:87:15:
a5:6d:71:8e:37:02:a2:85:0f:1e:19:75:a3:80:2e:6a:1a:a2:
02:8c:2f:ec:bd:3d:81:03:3f:8a:c0:a0:e6:b4:0e:08:57:cb:
00:1c:8a:b7:1b:8f:38:71:9a:8d:c0:71:0c:3f:bc:d4:be:56:
9d:f7:18:c1:aa:be:e4:df:1a:86:e2:62:6f:23:86:30:54:78:
2d:47:1f:b4:ad:05:29:73:24:98:14:a0:19:c0:02:fd:90:90:
4e:62:5c:e8:4d:31:89:c3:e8:8b:9e:73:59:3b:98:91:ca:47:
a5:05:5b:c5:1e:8f:85:39:0e:ce:b5:26:0a:80:4e:9f:08:4a:
11:49:13:63
5. 提取签名
openssl x509 -in baidu.crt -text -noout -certopt ca_default -certopt no_validity -certopt no_serial -certopt no_subject -certopt no_extensions -certopt no_signame | grep -v 'Signature Algorithm' | tr -d '[:space:]:' | xxd -r -p > baidu-signature.bin
6. 从中间证书提取公钥
openssl x509 -in inter-nv.crt -noout -pubkey > issuer-pub.pem
7. 使用公钥解密签名
openssl rsautl -verify -inkey issuer-pub.pem -in baidu-signature.bin -pubin > baidu-signature-decrypted.bin
8. 查看签名
将ASN.1数据解析并以易于阅读的格式显示出来
openssl asn1parse -inform DER -in baidu-signature-decrypted.bin
0:d=0 hl=2 l= 49 cons: SEQUENCE
2:d=1 hl=2 l= 13 cons: SEQUENCE
4:d=2 hl=2 l= 9 prim: OBJECT :sha256
15:d=2 hl=2 l= 0 prim: NULL
17:d=1 hl=2 l= 32 prim: OCTET STRING [HEX DUMP]:C344DC4526E5D6C357B7FF936995F07F3BA852F9F0F3F23CAA7228BAE87D9D60
8. 提取证书body
openssl asn1parse -in baidu.crt -strparse 4 -out baidu-body.bin &> /dev/null
9. 计算证书签名
可以看到和证书解析后看到一致。
openssl dgst -sha256 baidu-body.bin
SHA256(baidu-body.bin)= c344dc4526e5d6c357b7ff936995f07f3ba852f9f0f3f23caa7228bae87d9d60
这样就验证了从GlobalSign nv-sa签发baidu证书是完整的。整个证书链的验证其实就是重复这个过程。
/etc/ssl/certs/GlobalSign_Root_CA.pem => inter-r3.crt => inter-nv.crt => baidu.crt
参考 https://juejin.cn/post/6844904143719497735