4、CA搭建与自签名证书:搭建私有CA、生成CA根证书、配置CA策略、管理证书吊销列表(CRL)
说到MQTT通信的安全,证书是绕不开的一环。很多团队在开发阶段或者内部系统里,并不想花钱买商业CA的证书。这时候,自己搭一个私有CA,自己签证书,就成了最实在的选择。
我个人习惯把私有CA叫做“内部证书作坊”。它不对外公开,只给自己人用。你想想看,几十台物联网设备,每台都要配证书,如果全买商业的,成本扛不住。自己签,省钱又灵活。
4.1 搭建私有CA的核心思路
私有CA说白了就是三件事:生成根证书、签发中间证书或设备证书、管理吊销。我见过不少团队,根证书和服务器证书混在一起用,这是个大坑。根证书一旦泄露,整个信任链就崩了。
搭建之前,先理清角色:
- 根CA:最顶层的信任锚,离线保存,平时不碰它。
- 中间CA(可选):用来签发设备证书,万一泄露,可以单独吊销,不影响根。
- 终端实体证书:MQTT Broker、设备客户端用的就是它。
核心原则:根CA必须离线。我在项目中见过有人把根CA放在生产服务器上,结果被扫到私钥文件,整个PKI重建了一次。别犯同样的错。
4.2 生成CA根证书
我习惯用OpenSSL来干这个活。它虽然命令行参数多,但胜在稳定、可控。生成根证书分三步:
- 生成根CA私钥——建议用4096位RSA,别省这点性能。
- 生成根证书请求——其实根证书是自签名的,不需要请求,但OpenSSL流程上会走一遍。
- 自签名根证书——设置10年有效期,够用。
# 1. 生成根CA私钥(4096位,带加密)
openssl genrsa -aes256 -out ca.key 4096
# 2. 生成自签名根证书(有效期3650天)
openssl req -x509 -new -nodes -key ca.key \
-sha256 -days 3650 \
-out ca.crt \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyIoT/CN=MyRootCA"
小技巧:我习惯把根证书的有效期设到10年,中间证书5年,设备证书1-2年。这样根证书不用频繁更换,设备证书可以滚动更新。
这里有个细节:-nodes 参数表示私钥不加密。生成根证书时我建议去掉 -nodes,加上 -aes256 加密私钥。每次签证书都要输密码,虽然麻烦,但安全。
4.3 配置CA策略
CA策略说白了就是“什么证书能签,什么不能签”。OpenSSL通过一个配置文件来控制。我见过最惨的案例,就是没配策略,结果签出来的证书CN乱填,导致TLS握手验证失败。
下面是我常用的策略模板:
# openssl-ca.cnf
[ ca ]
default_ca = CA_default
[ CA_default ]
database = index.txt # 已签发证书数据库
serial = serial # 序列号文件
new_certs_dir = ./newcerts # 新证书存放目录
default_md = sha256
policy = policy_loose # 宽松策略,适合内部
[ policy_loose ]
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied # CN必须提供
emailAddress = optional
[ req ]
distinguished_name = req_distinguished_name
prompt = no
[ req_distinguished_name ]
C = CN
ST = Beijing
L = Beijing
O = MyIoT
CN = MyIntermediateCA
为什么用 policy_loose?因为物联网设备种类多,有些设备证书的字段填不全。如果策略太严,签证书时老报错。内部环境,宽松点反而省事。
注意:生产环境建议用 policy_match 严格模式。我曾经在客户现场吃过亏——宽松策略签出来的证书,被安全审计判定为“证书身份不可追溯”。
4.4 签发中间CA证书
根证书生成后,我建议再签一个中间CA。中间CA的私钥可以放在在线服务器上,根CA的私钥继续离线。
# 1. 生成中间CA私钥
openssl genrsa -out intermediate.key 2048
# 2. 生成中间CA证书请求
openssl req -new -key intermediate.key \
-out intermediate.csr \
-subj "/C=CN/ST=Beijing/O=MyIoT/CN=MyIntermediateCA"
# 3. 用根CA签发中间CA证书
openssl ca -config openssl-ca.cnf \
-extensions v3_intermediate_ca \
-days 1825 -notext -md sha256 \
-in intermediate.csr \
-out intermediate.crt
这里要注意 v3_intermediate_ca 扩展。不加这个扩展,中间CA签出来的证书浏览器不认。扩展内容如下:
[ v3_intermediate_ca ]
basicConstraints = critical, CA:TRUE, pathlen:0
keyUsage = critical, digitalSignature, keyCertSign, cRLSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
pathlen:0 表示中间CA下面不能再有CA了。这样即使中间CA被攻破,攻击者也签不出新的CA证书,风险可控。
4.5 管理证书吊销列表(CRL)
证书不是签出来就万事大吉了。设备报废、私钥泄露、员工离职,这些场景都需要吊销证书。CRL就是干这个的。
我刚开始做物联网时,觉得CRL可有可无。直到有一次,一批设备流出到非受控环境,私钥可能泄露。当时没有CRL,只能把所有证书重新换一遍,折腾了整整两天。从那以后,CRL成了我的标配。
4.5.1 初始化CRL环境
OpenSSL管理CRL需要几个文件:
# 创建必要的文件
touch index.txt # 证书数据库
echo 01 > serial # 序列号起始值
mkdir -p newcerts crl # 存放新证书和CRL的目录
4.5.2 生成初始CRL
# 生成空CRL(由根CA签发)
openssl ca -config openssl-ca.cnf \
-gencrl -out crl/ca.crl
# 查看CRL内容
openssl crl -in crl/ca.crl -text -noout
4.5.3 吊销证书
吊销证书时,需要知道证书的序列号或CN:
# 吊销证书(通过序列号)
openssl ca -config openssl-ca.cnf \
-revoke newcerts/01.pem \
-crl_reason keyCompromise
# 重新生成CRL(吊销后必须更新CRL)
openssl ca -config openssl-ca.cnf \
-gencrl -out crl/ca.crl
关键点:吊销证书后,必须重新生成CRL并分发到MQTT Broker。Broker在TLS握手时,会检查客户端证书是否在CRL中。如果在,直接拒绝连接。
4.5.4 CRL分发与更新
CRL不能只生成一次就不管了。我建议:
- 定期更新:每周生成一次CRL,即使没有新吊销的证书。
- 有效期设置:CRL有效期建议30天,过期后Broker会拒绝所有证书。
- 分发方式:通过HTTP、FTP或MQTT本身推送CRL到各节点。
| 参数 | 建议值 | 说明 |
|---|---|---|
| CRL有效期 | 30天 | 过期后Broker拒绝所有连接 |
| 更新频率 | 每周一次 | 即使无吊销,也要刷新 |
| CRL大小 | 建议<1MB | 物联网设备内存有限 |
| 签名算法 | SHA256 | SHA1已不安全 |
经验之谈:对于资源受限的物联网设备,CRL可能太大。我一般配合OCSP(在线证书状态协议)使用。设备只查单个证书状态,不用下载整个CRL。不过OCSP需要额外搭建响应服务器,看项目预算决定。
4.6 避坑指南
做了这么多年PKI,踩过的坑不少。挑几个典型的说说:
- 私钥权限:根CA私钥建议用HSM或加密U盘存储。我曾经见过有人把ca.key放在Git仓库里……嗯,那场面。
- 序列号管理:序列号文件serial必须手动维护。多线程签发时可能冲突,建议用脚本加锁。
- 时间同步:证书有效期依赖系统时间。设备时间不准,证书验证会失败。NTP一定要配好。
- CRL缓存:Broker会缓存CRL。更新CRL后,可能需要重启Broker或清缓存才能生效。
严重警告:不要在MQTT Broker上直接存储根CA私钥。Broker只需要根证书(ca.crt)来验证客户端,不需要私钥。私钥泄露等于整个信任链崩塌。
好了,私有CA的搭建就聊到这儿。这套流程我在多个物联网项目里验证过,稳定可靠。你按这个步骤走,内部证书管理基本不会出大问题。