数字证书原理:X.509 标准、证书结构与签名算法
说到数字证书,绕不开的就是 X.509 标准。我刚开始接触 PKI 那会儿,第一反应就是 —— 这玩意儿到底长什么样?里面都存了啥?谁签的?怎么验证?
说白了,X.509 就是定义了公钥证书的「通用格式」。不管你是浏览器、服务器还是智能卡,只要按这个格式来,大家就能互相认。没有它,各个 CA 签出来的证书就是一堆自说自话的数据。
X.509 标准 —— 数字证书的「通用语言」
X.509 是国际电信联盟(ITU-T)搞的标准,后来被 ISO/IEC 吸纳为 9594-8。它不只定义了证书格式,还包括证书吊销列表(CRL)和证书路径验证规则。不过我们最常用的,还是证书结构这块。
目前主流的是 X.509 v3 版本。v1 是 1988 年出的,字段少得可怜;v2 加了两个字段,但扩展性还是不行;到 v3 才算真正「开窍」,引入了扩展项(Extensions),这才让证书能支持各种使用场景 —— 比如代码签名、SSL/TLS、电子印章。
证书结构 —— 拆开看看里面有什么
X.509 证书的结构,从逻辑上可以分成两大块:TBS 证书(To Be Signed) 和 签名值。TBS 是「待签名数据」,签名值是「防伪封条」。
我在项目中遇到过好几次「证书结构解析失败」的情况 —— 十有八九是 TBS 和签名之间没对应上。嗯,这里要注意:签名算法字段在 TBS 里出现了一次,在证书外层又出现了一次,两者必须一致,否则验证直接挂。
下面是 X.509 v3 证书的核心字段清单:
| 字段 | 说明 | 常见值 / 示例 |
|---|---|---|
| Version | 证书版本号 | 0=v1, 1=v2, 2=v3 |
| Serial Number | CA 分配的唯 一编号 | 0x1A2B3C4D... |
| Signature Algorithm | TBS 签名使用的算法 | sha256WithRSAEncryption |
| Issuer | 颁发者 DN | CN=MyCA, O=Example, C=CN |
| Validity | 有效期(notBefore / notAfter) | 2024-01-01 ~ 2025-01-01 |
| Subject | 主体 DN | CN=server.example.com |
| Public Key Info | 公钥算法 + 公钥值 | RSA 2048 bits |
| Extensions [v3] | 扩展项(可选) | BasicConstraints, KeyUsage |
扩展项是 v3 的精髓。比如 BasicConstraints 标识这个证书能不能当 CA;KeyUsage 限制公钥的用途 —— 签名证书不能拿来加密,这是规矩。
id-kp-serverAuth,浏览器会直接报「证书用途错误」。我曾经帮人排查过一个线上问题——折腾了两天才发现是扩展项漏配了。
签名算法 —— 信任的数学基础
证书结构摆在那,谁来保证它没被篡改?就是签名算法。你可以把它想象成一个「数学封条」—— CA 用私钥把 TBS 证书的哈希值加密,附在证书末尾。谁拿到证书,用 CA 公钥解密,比对哈希就知道有没有被动过。
常见的签名算法组合有这么几类:
- RSA 系列: SHA-256 with RSA Encryption(最广,2048 位起步)
- ECDSA 系列: SHA-256 with ECDSA(效率高,密钥短,适合移动端)
- EdDSA: 新兴算法,性能和安全性都不错,但生态还在完善
我建议在实际项目中,优先选 SHA-256 配 RSA-2048 或 ECDSA P-256。SHA-1 已经不能用了,RSA-1024 也早该淘汰。
openssl x509 -in cert.pem -text -noout 跑一遍,看看 Signature Algorithm 字段和 TBS 里的是不是一致。这一步能挡住很多配置错误。
签名验证的过程,说白了就是三步:
- 用哈希算法对 TBS 证书算一遍摘要
- 用 CA 公钥对签名值做解密,得到原始摘要
- 两个摘要对比 —— 一致就过,不一致就拒
这个验证链会一级一级往上走,直到你手里那个「信任锚」——也就是根证书。根证书是自己签自己(自签名),它的可信度来自你提前把它装进了信任库。
最后说一点:签名算法不只体现在证书里,CRL、OCSP 响应也一样要用。你搭建 PKI 服务时,CA 的签名算法配置是全局的 —— 改一次,所有下游证书和吊销信息都得跟着变。选之前一定想清楚。