证书与 PKI 体系
聊到 mTLS,绕不开一个基础问题:你怎么证明“你”就是你?
我个人习惯把 PKI 看作数字世界的身份证系统。你想想看,现实生活中我们靠公安局发身份证,那么网络里就得靠一个可信的第三方来签发数字证书。这个体系,就叫公钥基础设施(PKI)。
PKI 的核心组件
PKI 不是单一的技术,而是一套完整的框架。我在项目中见过不少团队只配了证书,却忽略了 CRL(证书吊销列表)和 RA(注册机构),结果线上证书泄露后才发现吊销机制形同虚设。
下面这张表列出了 PKI 的关键部件:
| 组件 | 角色 | 一句话理解 |
|---|---|---|
| CA(证书颁发机构) | 签发与吊销证书 | 相当于公安局户籍科 |
| RA(注册机构) | 验证申请者身份 | 派出所的初审窗口 |
| 证书库 | 存储已签发证书与 CRL | 电子档案柜 |
| 密钥对 | 公钥 + 私钥 | 数字印章和印章盒 |
| 证书策略 | 规定证书用途与生命周期 | 用章制度手册 |
避坑提示:我曾经接手过一个遗留微服务项目,所有服务共用一套自签名根证书。你想想看,一旦根私钥泄露,整个集群都得重新建。后来我强制要求每个业务域创建独立中间 CA,这样即使某个域出问题,也不会波及全局。
CA 层级与证书链
现实中的 PKI 很少只有一个 CA。为什么呢?因为根 CA 太重要了,一旦私钥暴露,整个信任体系就崩塌。所以大部分生产环境都用三级甚至更多层级:根 CA → 中间 CA → 终端证书。
证书链,就是这条信任路径。从终端证书开始,逐级往上找签发者,直到自签名的根证书。我习惯用一个比喻:你收到一个快递,包裹上贴了三层标签。最外面是终端证书,撕开一层是中间 CA 签名,再撕开是根 CA 签名 —— 只有每一层都验证通过,才能确认包裹是真的。
关键点:证书链必须完整传递。微服务部署时,如果你只把终端证书放上去而漏了中间 CA,对方客户端会报“unable to get local issuer certificate”。我曾经排查了一下午,最后发现是少拷贝了一个 .pem 文件。
密钥对:公钥与私钥
非对称加密是 PKI 的数学基础。公钥可以公开,私钥必须保密。用公钥加密的数据只能用私钥解密,反之亦然 —— 这就是数字签名和加密通信的根基。
生成一个 2048 位的 RSA 密钥对,命令很简单:
# 生成私钥
openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048
# 从私钥提取公钥
openssl pkey -in private.pem -pubout -out public.pem
对了,私钥文件一定要设成 600 权限,而且不能提交到 Git 仓库。我见过有人把私钥跟代码一起打包到 Docker 镜像里……嗯,后面的事你懂的。
证书链验证流程
当服务 A 通过 mTLS 连接服务 B 时,B 会收到 A 的终端证书以及中间 CA 证书(如果有)。验证过程大致分三步:
- 解码证书:读取签发者字段,找到上一级 CA 证书。
- 校验签名:用上级 CA 的公钥解密终端证书的数字签名,比对摘要是否一致。
- 递归检查:重复步骤 1-2,直到遇到自签名的根证书。
用 OpenSSL 验证一条证书链可以这样:
openssl verify -CAfile root-ca.pem -untrusted intermediate.pem server.pem
如果输出 server.pem: OK,说明信任路径完整。我曾经遇到一个诡异问题:开发环境验证通过,预发布环境却报错。后来发现是预发布的根证书过期了 —— 证书链再完整,根失效等于零。
注意:证书链验证是 单向方向 的 —— 从终端证书向上追溯到根。但 mTLS 中双方都需要认证,所以实际上会走两条相反的验证路径。我曾经被这个问题坑过:只配了单向验证,结果服务 B 能验证服务 A,但 A 无法验证 B,握手直接失败。
掌握了 PKI 体系,你就理解了证书是怎么来的,怎么验证的,以及为什么需要链条。下一层知识是证书格式、编码和扩展字段 —— 那些是你调试 mTLS 时会直接面对的东西。