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签发) 客户端信任锚点 浏览器/系统预装根证书 私钥高度保密 中间层 承担实际签发工作 隔离根CA风险 验证过程 客户端先收到服务器证书 → 查看其颁发者 → 拿着颁发者DN去搜中间CA证书 → 验证签名 → 继续往上查,直到找到预装在系统里的根CA → 信任链成立

你想想看,这个链越长,信任传递就越复杂。但好的一面是,它把风险分散了。就算中间CA的私钥丢了,根CA也可以把这个中间CA拉黑,然后重新发个证。根CA的私钥如果丢了,那就只能重建整个体系了。

3.3 证书信任链——如何证明你妈是你妈

信任链的验证,说白了就是一层一层往上查,直到找到一个你本来就信任的根CA。

浏览器收到服务器发给它的证书后,不会直接相信。它会这么做:

  1. 提取颁发者信息:从服务器证书里拿到Issuer字段。
  2. 查找中间证书:浏览器会检查本地是否存有该颁发者对应的中间CA证书。如果在握手时服务器没有搭配发送中间CA证书(这是常犯的错误),浏览器可能只会从本地知识库去查。
  3. 验证签名:用中间CA的公钥去解密服务器证书的数字签名,算一下证书内容的哈希值和解密出来的值是不是对得上。对不上,就是证书被篡改了。
  4. 重复以上步骤:拿着中间CA证书,查它的颁发者,继续往上找。直到找到根CA。
  5. 检查信任锚点:这个根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的地基。你把它搞懂了,再看后面章节里关于证书配置、双向认证、以及证书吊销的那些内容,会顺手很多。嗯,就先说这么多。