3. 数字证书与PKI:X.509证书结构、CA体系、证书信任链
聊完了TLS握手协议,咱们得回过头来看一个更基础的问题——你怎么确定你连的那个服务器,就是它声称的那个?
这就引出了PKI,也就是公钥基础设施。说白了,它是一整套规则、流程和技术,用来管理公钥的生成、分发和注销。而数字证书,就是这套体系里最核心的身份凭证。
3.1 X.509证书结构——拆开看看里面到底有什么
我记得刚入行那会儿,第一次用OpenSSL查看证书内容,满屏的字段看得我头晕。后来拆多了,其实就那么几个关键部分。
X.509是国际电信联盟(ITU-T)定义的标准,目前用的是第三版,也就是X.509 v3。一个证书就是一组ASN.1结构编码后的数据,常见的是DER或PEM格式。你打开一个证书,会看到下面这些字段:
- 版本号(Version):告诉你这是v1、v2还是v3。现在基本都是v3。
- 序列号(Serial Number):CA分配给证书的唯一编号。这是我查吊销列表时最常用的线索。
- 签名算法ID(Signature Algorithm Identifier):CA是用什么算法签的这个证书。比如sha256WithRSAEncryption。
- 颁发者(Issuer):签这个证书的CA机构名称。写的是一个可分辨名称DN。
- 有效期(Validity):Not Before和Not After。我习惯把这个叫“出生日期”和“死亡日期”。
- 主体(Subject):证书属于谁。对于服务器证书,这里会写域名或公司名。
- 公钥信息(Subject Public Key Info):包含算法和公钥值。这是整个证书最有用的部分。
- 扩展(Extensions v3 only):从v3开始引入。说明说这个证书可以用在哪。最关键的一个扩展叫Subject Alternative Name (SAN),也就是证书绑定的域名列表。我在项目里遇到过好几次,部署完一看浏览器报错“证书名称不匹配”,十有八九是SAN里没加上这个域名。
用openssl命令看一眼证书内容:
openssl x509 -in example.crt -text -noout
你会看到一长串输出,中间那些字段就是我上面说的这些。咱们也可以用表格快速过一下:
| 字段名称 | 说明 | 常见取值示例 |
|---|---|---|
| Version | 证书版本 | v3 (0x2) |
| Serial Number | CA分配的唯一编号 | 04:12:ab:... (十六进制) |
| Signature Algorithm | 签名算法 | sha256WithRSAEncryption |
| Issuer | 颁发者的DN | C=US, O=Let's Encrypt, CN=R3 |
| Validity | 有效期 | Not Before: Aug 1 00:00:00 2024 GMT |
| Subject | 证书主体的DN | CN=www.example.com |
| Subject Public Key Info | 公钥算法和公钥值 | RSA (2048 bit) 或 EC (P-256) |
| Extensions | 扩展字段 | Basic Constraints, Key Usage, SAN |
个人小技巧:我查证书的时候,第一步看Issuer和Subject是不是一样。如果一样,说明这是个自签名证书。自签名证书在内部测试可以用,但生产环境千万别这么干,浏览器会把它当野鸡证书直接拒绝。
3.2 CA体系——谁来发这个证
证书不是谁都能发的。谁来当这个裁判?CA,也就是证书颁发机构。
你想想看,如果随便一个人都能给你发个证书,说“我是中国银行”,那互联网就乱套了。因此形成了一套层级分明的CA体系:
- 根CA(Root CA):最顶层的CA。它自己给自己签名,叫“自签名”。操作系统的信任列表里,就预装了一堆根CA的证书。它们的私钥是最高级别的机密。
- 中间CA(Intermediate CA):根CA一般不直接给网站发证,怕万一私钥泄露就全完了。它会先给二级CA签个证,让二级CA干活。你可以把中间CA理解成区域经理。
- 最终实体证书(End-entity / Leaf Certificate):就是我们部署在服务器上的证书,用来给浏览器验证身份。
我曾经在给客户做安全评估时,看到他们把根CA的私钥放在了生产服务器上,当时就出了一身冷汗。这不是危言耸听,一旦根CA私钥泄露,整个信任链就垮了。
看下面这个简化的结构图:
你想想看,这个链越长,信任传递就越复杂。但好的一面是,它把风险分散了。就算中间CA的私钥丢了,根CA也可以把这个中间CA拉黑,然后重新发个证。根CA的私钥如果丢了,那就只能重建整个体系了。
3.3 证书信任链——如何证明你妈是你妈
信任链的验证,说白了就是一层一层往上查,直到找到一个你本来就信任的根CA。
浏览器收到服务器发给它的证书后,不会直接相信。它会这么做:
- 提取颁发者信息:从服务器证书里拿到Issuer字段。
- 查找中间证书:浏览器会检查本地是否存有该颁发者对应的中间CA证书。如果在握手时服务器没有搭配发送中间CA证书(这是常犯的错误),浏览器可能只会从本地知识库去查。
- 验证签名:用中间CA的公钥去解密服务器证书的数字签名,算一下证书内容的哈希值和解密出来的值是不是对得上。对不上,就是证书被篡改了。
- 重复以上步骤:拿着中间CA证书,查它的颁发者,继续往上找。直到找到根CA。
- 检查信任锚点:这个根CA的证书,是不是已经预装在我的信任列表里?如果在,好,信任链完整,走下一步;如果不在……浏览器就给你弹大红屏警告了。
我曾经踩过的坑:在Nginx上配了服务器证书,结果忘记把中间CA证书也链上去。浏览器收到后,发现中间CA查不到,直接报错。当时排查了半天,才发现是nginx的ssl_trusted_certificate指向错了。你记住了,配置服务器证书时,一定要把完整的证书链(服务器证书 + 中间CA)一起配好。
下面我用自己的实践告诉你,怎么看证书链是否完整:
# 在Linux服务器上验证证书链
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt -untrusted intermediate.crt server.crt
成功的输出应该是:server.crt: OK。如果报错,它会告诉你哪个环节出了问题,比如unable to get local issuer certificate,说明中间CA没找到。
3.4 说说几个常见CA和它们的区别
现在市面上的CA很多,主流的大致分为几类。我个人一般根据使用场景来选:
| 类型 | 代表厂商/机构 | 特点 | 我推荐的使用场景 |
|---|---|---|---|
| 公共受信CA | DigiCert, Sectigo, GlobalSign | 历史悠久,信任度高,价格贵。 | 金融、政府、大型企业网站 |
| 免费自动化CA | Let's Encrypt, ZeroSSL | 完全自动,免费,有效期短(90天)。 | 个人博客、中小型网站、API服务 |
| 企业私有CA | Windows AD CS, EJBCA | 自建,只在内网用,不受公共信任。 | 企业内部系统、代码签名、VPN |
我个人习惯是,对外服务的网站优先用Let's Encrypt,配合acme.sh做自动续期,基本不用操心。内部系统用自己搭的CA,但记得要把根证书分发给所有客户端电脑,否则天天有人问“为什么访问你们系统显示不安全”。
好,证书的结构、CA的层级、信任链的验证逻辑,这几块东西就是整个PKI的地基。你把它搞懂了,再看后面章节里关于证书配置、双向认证、以及证书吊销的那些内容,会顺手很多。嗯,就先说这么多。