证书与 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 证书(如果有)。验证过程大致分三步:

  1. 解码证书:读取签发者字段,找到上一级 CA 证书。
  2. 校验签名:用上级 CA 的公钥解密终端证书的数字签名,比对摘要是否一致。
  3. 递归检查:重复步骤 1-2,直到遇到自签名的根证书。

用 OpenSSL 验证一条证书链可以这样:

openssl verify -CAfile root-ca.pem -untrusted intermediate.pem server.pem

如果输出 server.pem: OK,说明信任路径完整。我曾经遇到一个诡异问题:开发环境验证通过,预发布环境却报错。后来发现是预发布的根证书过期了 —— 证书链再完整,根失效等于零。

PKI 体系结构图:根CA、中间CA、终端证书与证书链验证 PKI 体系结构图 根 CA 自签名,信任锚点 中间 CA 1 签发终端证书 中间 CA 2 签发终端证书 服务A 证书 微服务 mTLS 服务B 证书 微服务 mTLS 用户 证书 客户端认证 证书链验证方向:终端证书 → 中间CA → 根CA(信任锚点) 每级使用上级公钥校验签名,直至自签名根证书

注意:证书链验证是 单向方向 的 —— 从终端证书向上追溯到根。但 mTLS 中双方都需要认证,所以实际上会走两条相反的验证路径。我曾经被这个问题坑过:只配了单向验证,结果服务 B 能验证服务 A,但 A 无法验证 B,握手直接失败。

掌握了 PKI 体系,你就理解了证书是怎么来的,怎么验证的,以及为什么需要链条。下一层知识是证书格式、编码和扩展字段 —— 那些是你调试 mTLS 时会直接面对的东西。