Back

/ 9 min read

TLS证书验证研究

使用OpenSLL手工模拟证书验证

证书验证的公式为: 加密算法(证书主体)=pow(证书签名值, CA证书公钥参数exponent, CA证书公钥modulus)

1. 看一下证书的信息

首先,我们有一个证书cppreference.crt,这是我们验证的目标证书。

Terminal window
openssl x509 -in cppreference.crt -text -noout # 查看证书内容
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
03:aa:56:73:8b:17:c7:40:02:94:df:ee:f9:35:cc:96:cc:55
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, O=Let's Encrypt, CN=R11
Validity
Not Before: Jul 15 07:30:51 2024 GMT
Not After : Oct 13 07:30:50 2024 GMT
Subject: CN=*.cppreference.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:e1:32:c1:05:92:7a:16:4c:eb:37:b3:bb:91:c1:
1f:9e:f7:41:16:8d:58:a6:1e:45:d9:a4:ca:93:1c:
fd:2b:f2:97:f1:e6:49:41:5a:2a:b3:75:07:79:96:
a5:60:da:68:4d:1e:df:60:22:16:11:80:30:c2:bf:
87:41:3c:d3:d6:91:80:de:b9:3b:e2:30:68:66:fe:
86:98:cd:e5:98:78:97:b2:0f:7e:ea:e8:c3:8d:c1:
a7:8e:2f:f9:61:f6:e4:14:51:2a:64:91:0f:dd:3e:
7a:70:aa:65:68:f2:e0:7c:91:65:33:d2:e1:cf:2a:
58:74:9c:4d:a5:19:e9:43:eb:94:eb:c5:33:ce:95:
c8:72:c2:8f:9d:b7:11:c5:25:f2:a1:fd:1c:ae:60:
4e:e4:10:2b:89:da:84:c3:0a:40:47:2a:07:3e:ea:
8b:92:e0:6b:79:87:65:6c:83:c7:62:6f:46:f9:49:
84:c8:8a:e6:f0:ae:e9:ae:c2:7b:b2:c0:82:5f:9d:
a4:b5:f5:c6:1f:c5:40:69:44:1d:16:3b:16:28:01:
a3:6b:77:06:03:f4:74:ea:fa:52:9d:c7:dc:14:ce:
fc:5c:7e:24:8f:18:c3:7e:91:c2:b2:3e:f2:73:c8:
58:08:ed:d3:f0:50:ca:65:c8:d5:da:6a:6e:0e:1f:
5a:cb
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Subject Key Identifier:
18:3F:BF:B7:5F:E7:98:31:29:1A:79:CE:0F:F4:C0:B0:83:74:5B:B6
X509v3 Authority Key Identifier:
C5:CF:46:A4:EA:F4:C3:C0:7A:6C:95:C4:2D:B0:5E:92:2F:26:E3:B9
Authority Information Access:
OCSP - URI:http://r11.o.lencr.org
CA Issuers - URI:http://r11.i.lencr.org/
X509v3 Subject Alternative Name:
DNS:*.cppreference.com, DNS:cppreference.com
X509v3 Certificate Policies:
Policy: 2.23.140.1.2.1
CT Precertificate SCTs:
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : 3F:17:4B:4F:D7:22:47:58:94:1D:65:1C:84:BE:0D:12:
ED:90:37:7F:1F:85:6A:EB:C1:BF:28:85:EC:F8:64:6E
Timestamp : Jul 15 08:30:51.371 2024 GMT
Extensions: none
Signature : ecdsa-with-SHA256
30:46:02:21:00:B4:D0:2D:4F:66:04:41:A2:22:99:4C:
2A:51:CE:C5:FE:26:7C:56:5B:12:3B:A0:33:A6:6B:FF:
D7:7E:92:C6:49:02:21:00:EE:F0:0D:9E:A3:AB:62:22:
DF:BE:75:89:2E:1D:CE:48:76:AC:EC:0E:A9:0F:B0:A7:
AB:BA:28:89:58:5E:E1:19
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : 19:98:10:71:09:F0:D6:52:2E:30:80:D2:9E:3F:64:BB:
83:6E:28:CC:F9:0F:52:8E:EE:DF:CE:4A:3F:16:B4:CA
Timestamp : Jul 15 08:30:51.389 2024 GMT
Extensions: none
Signature : ecdsa-with-SHA256
30:45:02:21:00:C7:8A:32:CE:6E:DC:F4:56:2E:09:A5:
5E:E0:C1:B8:82:C8:62:6C:AD:95:D5:E7:16:AA:46:E0:
DB:CA:21:DA:89:02:20:1D:61:E2:DF:81:97:63:99:A0:
07:AB:BB:EC:0E:1F:12:7E:FA:E7:2C:8E:FA:73:60:D5:
30:66:A7:7F:C7:B7:A5
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
26:6e:e3:c2:ad:e0:ea:0b:57:67:45:31:4d:6f:4a:0c:04:bb:
84:7c:04:6d:77:ff:43:8f:73:92:29:44:05:2d:18:b5:18:2d:
ce:a2:32:44:fc:0a:ec:79:e8:68:05:be:51:85:b8:ef:e7:8c:
45:86:8d:30:c2:69:a1:b3:6d:0e:4f:7d:0b:37:64:18:1a:c5:
78:c2:ca:d1:22:1d:67:5f:4d:57:54:ac:44:9f:e4:10:f1:22:
4f:76:e3:f7:1d:cd:43:b7:a4:0f:5b:83:5f:d5:ae:23:38:09:
c9:1c:76:20:e9:80:0d:d0:6f:02:5d:17:aa:3b:76:39:b7:f3:
d9:f3:e2:50:6e:df:8d:a5:87:88:65:5c:c7:ba:8c:d4:e1:4e:
b8:8f:70:22:7e:41:68:22:34:5a:fd:02:52:c8:eb:46:e7:06:
e7:dd:15:7a:e2:39:39:3c:c5:80:0e:66:23:e1:c0:fc:d7:db:
3c:03:8b:1d:6e:8c:56:11:41:e4:4e:93:79:70:48:d4:e0:73:
85:71:56:3f:ae:9c:31:99:3c:04:f0:ac:bd:01:a5:e5:04:0c:
38:eb:0b:27:c7:98:8f:66:d4:60:a1:c2:b3:4a:81:3d:2b:4d:
a1:c5:c3:c3:2f:3b:35:29:81:dd:34:aa:41:63:74:10:49:f3:
20:31:bb:fa

其中对我们有用的字段:

  1. Signature Algorithm: sha256WithRSAEncryption。这表示此证书的签名算法是SHA256RSA
  2. Signature Value: 证书的签名值。我们需要验证这个签名值是否正确。
  3. CA Issuers: http://r11.i.lencr.org/。证书签发者的信息。我们需要获取签发者的公钥来验证证书的签名值。

2. 获取证书主体

根据第一步,我们知道了证书的加密算法是SHA256RSA,所以我们需要获取证书的主体信息。证书的主体信息是一个DER编码的数据,我们可以通过openssl工具来获取证书主体的偏移量和长度,将它保存到文件中。

Terminal window
openssl asn1parse -inform PEM -in cppreference.crt

通过openssl命令,查看PEM格式的证书的DER编码数据。

0:d=0 hl=4 l=1287 cons: SEQUENCE
4:d=1 hl=4 l=1007 cons: SEQUENCE
8:d=2 hl=2 l= 3 cons: cont [ 0 ]
10:d=3 hl=2 l= 1 prim: INTEGER :02
13:d=2 hl=2 l= 18 prim: INTEGER :03AA56738B17C7400294DFEEF935CC96CC55
33:d=2 hl=2 l= 13 cons: SEQUENCE
35:d=3 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
46:d=3 hl=2 l= 0 prim: NULL
48:d=2 hl=2 l= 51 cons: SEQUENCE
50:d=3 hl=2 l= 11 cons: SET
52:d=4 hl=2 l= 9 cons: SEQUENCE
54:d=5 hl=2 l= 3 prim: OBJECT :countryName
59:d=5 hl=2 l= 2 prim: PRINTABLESTRING :US
63:d=3 hl=2 l= 22 cons: SET
65:d=4 hl=2 l= 20 cons: SEQUENCE
67:d=5 hl=2 l= 3 prim: OBJECT :organizationName
72:d=5 hl=2 l= 13 prim: PRINTABLESTRING :Let's Encrypt
87:d=3 hl=2 l= 12 cons: SET
89:d=4 hl=2 l= 10 cons: SEQUENCE
91:d=5 hl=2 l= 3 prim: OBJECT :commonName
96:d=5 hl=2 l= 3 prim: PRINTABLESTRING :R11
101:d=2 hl=2 l= 30 cons: SEQUENCE
103:d=3 hl=2 l= 13 prim: UTCTIME :240715073051Z
118:d=3 hl=2 l= 13 prim: UTCTIME :241013073050Z
133:d=2 hl=2 l= 29 cons: SEQUENCE
135:d=3 hl=2 l= 27 cons: SET
137:d=4 hl=2 l= 25 cons: SEQUENCE
139:d=5 hl=2 l= 3 prim: OBJECT :commonName
144:d=5 hl=2 l= 18 prim: UTF8STRING :*.cppreference.com
164:d=2 hl=4 l= 290 cons: SEQUENCE
168:d=3 hl=2 l= 13 cons: SEQUENCE
170:d=4 hl=2 l= 9 prim: OBJECT :rsaEncryption
181:d=4 hl=2 l= 0 prim: NULL
183:d=3 hl=4 l= 271 prim: BIT STRING
458:d=2 hl=4 l= 553 cons: cont [ 3 ]
462:d=3 hl=4 l= 549 cons: SEQUENCE
466:d=4 hl=2 l= 14 cons: SEQUENCE
468:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Key Usage
473:d=5 hl=2 l= 1 prim: BOOLEAN :255
476:d=5 hl=2 l= 4 prim: OCTET STRING [HEX DUMP]:030205A0
482:d=4 hl=2 l= 29 cons: SEQUENCE
484:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Extended Key Usage
489:d=5 hl=2 l= 22 prim: OCTET STRING [HEX DUMP]:301406082B0601050507030106082B06010505070302
513:d=4 hl=2 l= 12 cons: SEQUENCE
515:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Basic Constraints
520:d=5 hl=2 l= 1 prim: BOOLEAN :255
523:d=5 hl=2 l= 2 prim: OCTET STRING [HEX DUMP]:3000
527:d=4 hl=2 l= 29 cons: SEQUENCE
529:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Subject Key Identifier
534:d=5 hl=2 l= 22 prim: OCTET STRING [HEX DUMP]:0414183FBFB75FE79831291A79CE0FF4C0B083745BB6
558:d=4 hl=2 l= 31 cons: SEQUENCE
560:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Authority Key Identifier
565:d=5 hl=2 l= 24 prim: OCTET STRING [HEX DUMP]:30168014C5CF46A4EAF4C3C07A6C95C42DB05E922F26E3B9
591:d=4 hl=2 l= 87 cons: SEQUENCE
593:d=5 hl=2 l= 8 prim: OBJECT :Authority Information Access
603:d=5 hl=2 l= 75 prim: OCTET STRING [HEX DUMP]:3049302206082B060105050730018616687474703A2F2F7231312E6F2E6C656E63722E6F7267302306082B060105050730028617687474703A2F2F7231312E692E6C656E63722E6F72672F
680:d=4 hl=2 l= 47 cons: SEQUENCE
682:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Subject Alternative Name
687:d=5 hl=2 l= 40 prim: OCTET STRING [HEX DUMP]:302682122A2E6370707265666572656E63652E636F6D82106370707265666572656E63652E636F6D
729:d=4 hl=2 l= 19 cons: SEQUENCE
731:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Certificate Policies
736:d=5 hl=2 l= 12 prim: OCTET STRING [HEX DUMP]:300A3008060667810C010201
750:d=4 hl=4 l= 261 cons: SEQUENCE
754:d=5 hl=2 l= 10 prim: OBJECT :CT Precertificate SCTs
766:d=5 hl=3 l= 246 prim: OCTET STRING [HEX DUMP]:0481F300F10077003F174B4FD7224758941D651C84BE0D12ED90377F1F856AEBC1BF2885ECF8646E00000190B58417EB0000040300483046022100B4D02D4F660441A222994C2A51CEC5FE267C565B123BA033A66BFFD77E92C649022100EEF00D9EA3AB6222DFBE75892E1DCE4876ACEC0EA90FB0A7ABBA2889585EE1190076001998107109F0D6522E3080D29E3F64BB836E28CCF90F528EEEDFCE4A3F16B4CA00000190B58417FD0000040300473045022100C78A32CE6EDCF4562E09A55EE0C1B882C8626CAD95D5E716AA46E0DBCA21DA8902201D61E2DF81976399A007ABBBEC0E1F127EFAE72C8EFA7360D53066A77FC7B7A5
1015:d=1 hl=2 l= 13 cons: SEQUENCE
1017:d=2 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
1028:d=2 hl=2 l= 0 prim: NULL
1030:d=1 hl=4 l= 257 prim: BIT STRING

第一部分是证书主体:

4:d=1 hl=4 l=1007 cons: SEQUENCE

证书主体的偏移为4,头部长度为4,长度为1007。它的总长度是4 + 1007 = 1011。接下来我们保存这个主体信息到文件中。

Terminal window
openssl asn1parse -inform PEM -in cppreference.crt -strparse 4 -out cppreference.tbs

3. 计算证书主体的哈希值

我们需要计算证书主体的哈希值,以便后续验证证书的签名值。我们使用SHA256算法来计算证书主体的哈希值。

Terminal window
sha256sum cppreference.tbs

得到输出:

6789b428b183bb801f58f13a0b49ccc06f155e49d14cddfd0e82fa1abdc398ec cppreference.tbs

自此,我们得到了公式的左侧,接下来我们要使用CA证书来计算公式的右侧。

4. 下载CA证书

在第一步中,我们得到了CA证书的下载地址http://r11.i.lencr.org/。我们可以通过curl工具来下载CA证书。

Terminal window
curl http://r11.i.lencr.org/ -o ca.der

5. 获取CA证书的公钥

我们需要获取CA证书的公钥,以便后续验证证书的签名值。我们可以通过openssl工具来获取CA证书的公钥。 CA证书的公钥部分通过先找到rsaEncryption字段,然后找到它下方的BIT STRING字段,这个字段就是CA证书的公钥。

Terminal window
openssl asn1parse -i -inform DER -in ca.der | grep -A 2 "rsaEncryption"

得到:

219:d=4 hl=2 l= 9 prim: OBJECT :rsaEncryption
230:d=4 hl=2 l= 0 prim: NULL
232:d=3 hl=4 l= 271 prim: BIT STRING

其中BIT STRING字段的偏移量为232,长度为271。我们可以通过openssl工具来提取CA证书的公钥。

Terminal window
openssl asn1parse -inform DER -in ca.der --strparse 232 -noout -out ca.rsa # 提取CA证书的公钥
openssl asn1parse -i -inform DER -in ca.rsa # 查看CA证书的公钥部分

查看后得到输出:

0:d=0 hl=4 l= 266 cons: SEQUENCE
4:d=1 hl=4 l= 257 prim: INTEGER :BA87BC5C1B0039CBCA0ACDD46710F9013CA54EA561CB26CA52FB1501B7B928F5281EED27B324183967090C08ECE03AB03B770EBDF3E53954410C4EAE41D69974DE51DBEF7BFF58BDA8B713F6DE31D5F272C9726A0B8374959C4600641499F3B1D922D9CDA892AA1C267A3FFEEF58057B089581DB710F8EFBE33109BB09BE504D5F8F91763D5A9D9E83F2E9C466B3E106664348188065A037189A9B843297B1B2BDC4F815009D2788FBE26317966C9B27674BC4DB285E69C279F0495CE02450E1C4BCA105AC7B406D00B4C2413FA758B82FC55C9BA5BB099EF1FEEBB08539FDA80AEF45C478EB652AC2CF5F3CDEE35C4D1BF70B272BAA0B4277534F796A1D87D9
265:d=1 hl=2 l= 3 prim: INTEGER :010001

其中:

  1. 第一个INTEGER字段是CA证书的公钥Modulus
  2. 第二个INTEGER字段是CA证书的公钥Exponent

6. 获取证书的签名值

接下来回到第一步中,我们得到了证书的签名值。我们需要提取证书的签名值。

Terminal window
openssl x509 -in cppreference.crt -text -noout | sed -n '/Signature Value:/,/^$/p' | sed 's/^ *//' | grep -v "Signature Value:" | tr -d ': \n'

获得输出:

266ee3c2ade0ea0b576745314d6f4a0c04bb847c046d77ff438f73922944052d18b5182dcea23244fc0aec79e86805be5185b8efe78c45868d30c269a1b36d0e4f7d0b3764181ac578c2cad1221d675f4d5754ac449fe410f1224f76e3f71dcd43b7a40f5b835fd5ae233809c91c7620e9800dd06f025d17aa3b7639b7f3d9f3e2506edf8da58788655cc7ba8cd4e14eb88f70227e416822345afd0252c8eb46e706e7dd157ae239393cc5800e6623e1c0fcd7db3c038b1d6e8c561141e44e93797048d4e0738571563fae9c31993c04f0acbd01a5e5040c38eb0b27c7988f66d460a1c2b34a813d2b4da1c5c3c32f3b352981dd34aa4163741049f32031bbfa

这个就是我们需要的证书的签名值。

7. 计算匹配

通过公式: 加密算法(证书主体)=pow(证书签名值, CA证书公钥参数exponent, CA证书公钥modulus) 我们编写一个python脚本来计算:

m=0xBA87BC5C1B0039CBCA0ACDD46710F9013CA54EA561CB26CA52FB1501B7B928F5281EED27B324183967090C08ECE03AB03B770EBDF3E53954410C4EAE41D69974DE51DBEF7BFF58BDA8B713F6DE31D5F272C9726A0B8374959C4600641499F3B1D922D9CDA892AA1C267A3FFEEF58057B089581DB710F8EFBE33109BB09BE504D5F8F91763D5A9D9E83F2E9C466B3E106664348188065A037189A9B843297B1B2BDC4F815009D2788FBE26317966C9B27674BC4DB285E69C279F0495CE02450E1C4BCA105AC7B406D00B4C2413FA758B82FC55C9BA5BB099EF1FEEBB08539FDA80AEF45C478EB652AC2CF5F3CDEE35C4D1BF70B272BAA0B4277534F796A1D87D9
e=0x010001
sig=0x266ee3c2ade0ea0b576745314d6f4a0c04bb847c046d77ff438f73922944052d18b5182dcea23244fc0aec79e86805be5185b8efe78c45868d30c269a1b36d0e4f7d0b3764181ac578c2cad1221d675f4d5754ac449fe410f1224f76e3f71dcd43b7a40f5b835fd5ae233809c91c7620e9800dd06f025d17aa3b7639b7f3d9f3e2506edf8da58788655cc7ba8cd4e14eb88f70227e416822345afd0252c8eb46e706e7dd157ae239393cc5800e6623e1c0fcd7db3c038b1d6e8c561141e44e93797048d4e0738571563fae9c31993c04f0acbd01a5e5040c38eb0b27c7988f66d460a1c2b34a813d2b4da1c5c3c32f3b352981dd34aa4163741049f32031bbfa
print("%x"%pow(sig, e, m))

输出结果:

1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004206789b428b183bb801f58f13a0b49ccc06f155e49d14cddfd0e82fa1abdc398ec

输出的结果中包含子串:6789b428b183bb801f58f13a0b49ccc06f155e49d14cddfd0e82fa1abdc398ec,此时证书的签名值和证书主体的哈希值是一致的,证明了证书的签名值是正确的。