3. 证书颁发机构(CA):信任的起点与层级设计

说到CA,很多人第一反应就是「花钱买证书的地方」。其实不然。CA的核心角色,是建立信任的锚点。你想想看,互联网上两个素未谋面的实体,凭什么相信对方出示的证书是真的?就凭CA在中间做了担保。

我个人习惯把CA比作「数字世界的公证处」。公证处不能乱发公证书,CA也不能乱签证书。一旦CA的私钥泄露,或者签发流程有漏洞,整个信任链就崩塌了。我在项目中遇到过一家企业,内部CA的根证书私钥直接明文存在一台测试服务器上……嗯,那场面,简直是给攻击者送大礼。

3.1 CA的角色与信任模型

CA承担三个核心职责:

  • 身份验证:确认申请证书的实体确实是它声称的那个实体
  • 证书签发:用CA自己的私钥为申请者生成数字证书
  • 生命周期管理:包括证书续期、吊销(CRL/OCSP)、归档

信任模型主要有三种:

信任模型 特点 典型场景
层次模型 根CA → 中间CA → 终端证书,单向信任链 公开CA(如DigiCert、Let's Encrypt)
网状模型 多个CA互相交叉认证,双向信任 企业内部多部门互信
桥CA模型 一个桥CA连接多个独立的PKI域 跨组织、跨行业的联邦信任

实际生产环境中,层次模型用得最多。为什么?因为它好管理、好审计。根CA可以离线保存,中间CA负责日常签发,即使中间CA被攻破,根CA还在,可以吊销中间CA证书重新签发。我曾经帮一家金融客户做PKI改造,他们之前用的是单层CA——根CA直接签终端证书,结果根CA私钥必须在线,风险极高。改造成两层后,安全等级直接上了一个台阶。

核心原则:根CA必须离线,中间CA在线签发。这是PKI安全的第一条铁律。

3.2 自签名CA搭建实战

搭建一个自签名CA,说白了就是自己生成一对根密钥,然后自己给自己签一张根证书。下面我用OpenSSL演示,这是最通用的工具。

3.2.1 生成根CA私钥

# 生成RSA 4096位根CA私钥,加密存储
openssl genrsa -aes256 -out rootCA.key 4096

# 查看私钥信息
openssl rsa -in rootCA.key -text -noout

这里我建议用-aes256参数加密私钥。每次使用根CA私钥时都需要输入密码,虽然麻烦一点,但安全多了。我曾经见过有人图省事生成无密码的私钥,结果运维人员误操作把私钥文件传到了公网Git仓库……那真是灾难级的泄露。

3.2.2 生成根证书(自签名)

# 生成自签名根证书,有效期10年
openssl req -x509 -new -nodes -key rootCA.key \
  -sha256 -days 3650 \
  -out rootCA.crt \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/CN=MyRootCA"

注意几个关键点:

  • -x509:表示输出自签名证书,而不是证书签名请求
  • -days 3650:根证书有效期通常设5-10年,太长有风险,太短管理成本高
  • -subj:证书主题,CN(Common Name)要能唯一标识这个CA

小技巧:根证书的CN建议加上「Root CA」字样,方便后续区分。比如CN=MyCompany Root CA,一目了然。

3.2.3 生成中间CA

根CA搭好后,别急着用它签终端证书。先创建一个中间CA:

# 1. 生成中间CA私钥
openssl genrsa -aes256 -out intermediate.key 4096

# 2. 生成中间CA证书签名请求(CSR)
openssl req -new -key intermediate.key \
  -out intermediate.csr \
  -subj "/C=CN/ST=Beijing/O=MyCompany/CN=MyIntermediateCA"

# 3. 用根CA签发中间CA证书
openssl x509 -req -in intermediate.csr \
  -CA rootCA.crt -CAkey rootCA.key \
  -CAcreateserial -out intermediate.crt \
  -days 1825 -sha256 \
  -extensions v3_ca -extfile openssl.cnf

这里-extensions v3_ca是关键。它告诉OpenSSL:这张证书是一个CA证书,可以继续签发下级证书。如果不加这个扩展,中间CA签发的证书在客户端验证时会报错。

避坑指南:我曾经遇到一个案例,中间CA证书没有设置pathLenConstraint,结果下级又签了一个下级CA,导致信任链无限延伸。建议在v3_ca扩展中加上pathLenConstraint:0,限制中间CA只能签终端证书,不能再签下级CA。

3.3 根证书与中间证书的层级关系

层级关系,说白了就是一条信任链。客户端验证终端证书时,会沿着证书链一路向上找,直到找到本地信任的根证书。下面这张图展示了典型的二级CA结构:

根CA与中间CA层级关系图 根CA与中间CA层级关系 根CA (Root CA) 离线保存 · 自签名 签发 中间CA (Intermediate CA) 在线签发 · 受根CA信任 签发 终端证书 (End-Entity) 服务器/客户端证书 信任链验证流程 1. 客户端收到终端证书 2. 获取签发者=中间CA 3. 获取中间CA证书 4. 获取签发者=根CA 5. 根CA在本地信任库中 6. 验证根CA自签名 验证通过 ✓ 如果中间环节失败 浏览器显示"不安全" 或证书错误 信任锚点 策略控制层 业务使用层

层级设计的好处很明显:

  • 安全隔离:根CA私钥可以放在离线机器甚至硬件加密机里,日常签发用中间CA
  • 灵活吊销:中间CA泄露了,只需要吊销中间CA证书,根CA不受影响
  • 策略分离:不同中间CA可以设置不同的证书策略,比如一个用于SSL,一个用于代码签名

实际部署时,我建议至少用两层。小型企业可以用两层(根CA + 中间CA),大型企业可以用三层(根CA + 策略CA + 签发CA)。层数越多,灵活性越高,但管理复杂度也成倍增加。我个人习惯是:能用两层解决的问题,绝不用三层。

经验之谈:根证书的有效期我一般设10年,中间CA设5年,终端证书设1-2年。这样既保证了根CA的稳定性,又通过终端证书的频繁轮换降低了单点风险。

3.4 证书链的验证机制

客户端验证证书链时,会做以下几件事:

  1. 从终端证书开始,读取Issuer字段,找到签发它的CA证书
  2. 用CA证书的公钥验证终端证书的签名
  3. 重复步骤1-2,直到找到自签名的根证书
  4. 检查根证书是否在本地信任存储中
  5. 检查所有证书是否在有效期内、是否被吊销

这里有个容易踩的坑:证书链不完整。有些服务器只配置了终端证书,没有附带中间CA证书。客户端如果没有中间CA证书,就无法构建完整的信任链。我曾经排查过一个案例,某电商网站的证书在Chrome上正常,但在老旧Android浏览器上报错——就是因为旧浏览器没有缓存中间CA证书,而服务器又没有下发完整的证书链。

避坑指南:服务器配置证书时,一定要把终端证书和中间CA证书拼接在一起下发。Nginx配置中ssl_certificate指向的文件应该包含完整的证书链,顺序是:终端证书 → 中间CA证书 → 根证书(可选)。