数字签名与证书:X.509 证书体系
数字签名和证书,是平台工程里最容易被忽视,但又最要命的基础设施。说白了,它就是系统的“身份证”加上“签名笔”。
我记得刚接手一个内部组件仓库时,发现所有开发者上传的包都没有签名。结果某天供应链攻击,一个恶意包被偷偷替换,下游三个服务全部中招。从那以后,我对签名和证书的敬畏心就拉满了。
X.509 证书体系
X.509 是公钥证书的国际标准格式。你看到的 HTTPS 证书、代码签名证书、客户端认证证书,底层都是它。
一个标准的 X.509 v3 证书包含以下核心字段:
| 字段 | 说明 | 个人踩坑点 |
|---|---|---|
| 版本号 (Version) | 目前主流是 v3,支持扩展项 | 别用 v1/v2,扩展项都用不了 |
| 序列号 (Serial Number) | CA 签发的唯一编号 | 我曾经遇到过序列号重复的诡异问题,后来发现是内部 CA 没做原子递增 |
| 签名算法 (Signature Algorithm) | 如 sha256WithRSAEncryption | SHA-1 已经在很多浏览器里被标记为不安全了 |
| 签发者 (Issuer) | CA 机构的 DN | 注意 DN 的层级顺序,区分根 CA 和中间 CA |
| 有效期 (Validity) | notBefore / notAfter | 证书过期后的连锁故障,我经历过——整个 CI 全部挂掉 |
| 主体 (Subject) | 证书持有者的 DN | SAN(主题备用名称)比 CN 更重要,现在是标准做法 |
| 公钥 (Public Key) | RSA/ECC 的公钥信息 | ECC 的密钥短很多,性能也更好,我个人的新项目首选 ECDSA |
| 扩展项 (Extensions) | 如 Key Usage、CA、Subject Alt Name | 这地方坑最多,用错了签名验证直接失败 |
核心原则:证书链的信任是传递的。你只信任根 CA,根 CA 签发中间 CA,中间 CA 再签发终端证书。这个链条一旦断裂,整个信任体系就崩了。
数字签名流程
我们聊聊签名到底怎么工作的。很多人觉得签名就是“加密”,其实不是。它是“哈希 + 私钥加密”的组合拳。
签名过程(你签)
- 对原始数据做哈希运算,得到摘要(比如 SHA-256)。
- 用你的私钥对这个摘要加密,得到数字签名。
- 把原始数据、签名、你的证书一起发给对方。
验签过程(对方验)
- 用你的证书里的公钥解密签名,得到摘要A。
- 对原始数据自己做哈希,得到摘要B。
- 比较A和B。相等则签名有效,数据未被篡改。
你想想看,如果数据被篡改了,哈希值肯定对不上。如果私钥泄露了,那签名就完全不可信了。所以我常说:私钥就是命根子。
# 用 OpenSSL 做签名和验签的典型命令
# 1. 生成私钥(RSA 2048位)
openssl genrsa -out private.pem 2048
# 2. 生成公钥
openssl rsa -in private.pem -pubout -out public.pem
# 3. 签名文件 data.txt
openssl dgst -sha256 -sign private.pem -out data.sig data.txt
# 4. 验签
openssl dgst -sha256 -verify public.pem -signature data.sig data.txt
提示:实际生产环境不要用 RSA 2048 了。我个人推荐 ECDSA 搭配 P-256 曲线,同等安全强度下,性能和密钥长度都更优。验签速度能快好几倍,尤其在 IoT 或移动端场景里非常明显。
代码签名实践
代码签名,就是把数字签名技术用在软件制品上。为什么需要代码签名?
- 完整性:确保代码在传输或存储过程中没被篡改。
- 来源可信:接收方能确认代码确实来自声称的发布者。
- 平台要求:Windows、macOS、Android、iOS 都要求应用必须签名才能安装或运行。
我曾经遇到过一个场景:内部团队在 CI 里直接用开发人员本地的私钥签名。结果一个开发者的机器被入侵,攻击者拿到了私钥,开始在仓库里投毒。后来我们强制迁移到硬件安全模块(HSM)或云 KMS 管理私钥,才彻底解决了这个问题。
代码签名的标准流程
- 准备证书:从受信任的 CA 申请代码签名证书。个人或开发阶段可以用自签名证书,但生产环境必须用 CA 签发的。
- 保护私钥:私钥存放到 HSM、KMS 或安全密码箱里。绝不在 CI 的明文环境变量里放私钥。
- 计算哈希:对要签名的文件(如 .jar、.dll、.exe)计算哈希。
- 签名:用私钥对哈希签名,生成签名文件或嵌入到文件中(如 Authenticode 签名嵌入 PE 文件里)。
- 分发:将签名后的制品分发给用户或部署到服务器。
下面是一个典型的代码签名在 CI 中的集成示意:
警告:代码签名的私钥一旦泄露,攻击者可以用它签发任何恶意代码。你的整个软件供应链信誉会在几分钟内崩塌。我建议:
- 使用硬件安全模块(HSM)或云 KMS(如 AWS KMS、Azure Key Vault)存储私钥。
- 签名的操作在 CI 中通过 KMS API 完成,私钥永远不离开安全边界。
- 私钥的访问权限严格管控,并开启审计日志。
另外,证书的有效期也要注意。很多团队签完代码后就不管了,等到证书过期才发现所有新版本都无法通过验证。更糟的是,如果证书被吊销(比如私钥泄露),之前用这个证书签的所有代码都会立刻变成“不受信任”的。
所以我的建议是:
- 证书有效期不要太长。个人习惯:代码签名证书一年一换。
- 建立证书到期前的自动告警机制。
- 如果旧证书被吊销,尽快用新证书重新签名所有可执行文件。
提示:对于内部平台,可以搭建私有 CA 来签发内部代码签名证书。这样做的好处是:你可以完全控制信任链,不需要依赖外部 CA。但坏处是:你得自己管好根 CA 的私钥——一旦丢了,所有内部颁发的证书都得作废。