3. OpenSSL 工具速览与 CA 搭建
说实话,很多搞 MQTT 的人,最后都栽在证书上。不是协议多难,而是 OpenSSL 这把瑞士军刀,太多人只会用其中一两招。今天我们就把它摊开来看清楚。
我个人习惯,不管项目多急,都会先搭一套私有根 CA。为什么?因为你想想看,生产环境里如果每台设备都去申请付费证书,成本高不说,管理起来也头疼。自己搭一个 Root CA,一劳永逸。
3.1 OpenSSL 核心命令速览
OpenSSL 是个命令行工具箱。我把它分成三类,你记牢这三类就够了:
| 类别 | 常用命令 | 一句话说明 |
|---|---|---|
| 密钥生成 | genrsa / ecparam |
生成 RSA 或 ECC 私钥 |
| 证书操作 | req / x509 / ca |
生成 CSR、自签证书、颁发证书 |
| 验证与查看 | verify / s_client |
验证证书链、测试 TLS 握手 |
我在项目中遇到过最典型的场景:现场工程师拿着一个 .pem 文件,不知道是公钥还是私钥。其实用一行命令就能看清——openssl x509 -in file.pem -text -noout,如果是证书,会打印出完整信息;如果是私钥,会报错。这招很实用。
openssl asn1parse -in file.pem 看看 ASN.1 结构,能快速判断是密钥、证书还是 CSR。
3.2 配置文件解析——openssl.cnf
很多人觉得 OpenSSL 的配置文件又臭又长。嗯,确实。但你不需要全看懂,只需要抓住几个关键段。
我一般会准备一个精简版的配置文件,放在项目目录下。核心结构是这样的:
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
string_mask = utf8only
prompt = no
[ req_distinguished_name ]
countryName = CN
stateOrProvinceName = Beijing
localityName = Haidian
organizationName = MyIoT
organizationalUnitName = MQTT Gateway
commonName = mqtt-gateway.local
emailAddress = admin@myiot.com
[ v3_ca ]
basicConstraints = critical, CA:TRUE
keyUsage = critical, keyCertSign, cRLSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = mqtt-gateway.local
DNS.2 = localhost
IP.1 = 192.168.1.100
这里我重点说三个坑:
- commonName:以前浏览器还认 CN,现在 Chrome 只认 SAN。但 MQTT 客户端很多还依赖 CN,所以我两个都填。
- CA:TRUE vs CA:FALSE:根证书必须写
CA:TRUE,服务器证书必须写CA:FALSE。写反了,整个证书链就废了。 - subjectAltName:MQTT 网关如果通过 IP 连接,一定要加
IP.1。我曾经遇到一个项目,所有配置都对,就是忘了加 SAN,结果客户端一直报证书验证失败。查了整整一下午。
default_bits 建议用 4096。虽然生成慢一点,但安全性更高。IoT 设备如果性能受限,至少也要 2048。
3.3 搭建私有根 CA(Root CA)
好,现在我们来动手。搭建私有根 CA,说白了就是三步:生成密钥、自签证书、准备目录结构。
3.3.1 准备目录结构
我习惯用这个结构,清晰又好管理:
my-ca/
├── certs/ # 存放已颁发的证书
├── crl/ # 证书吊销列表
├── newcerts/ # OpenSSL 自动生成的新证书
├── private/ # CA 私钥(权限 600)
├── index.txt # 证书数据库
└── serial # 序列号文件
创建命令:
mkdir -p my-ca/{certs,crl,newcerts,private}
chmod 700 private
touch my-ca/index.txt
echo 1000 > my-ca/serial
这里有个细节:serial 文件初始值建议从 1000 开始,这样以后颁发的证书编号整齐,方便追溯。
3.3.2 生成根 CA 密钥
openssl genrsa -aes256 -out my-ca/private/ca.key.pem 4096
chmod 400 my-ca/private/ca.key.pem
为什么要加 -aes256?因为根 CA 私钥是整条信任链的命根子。一旦泄露,你颁发的所有证书都得作废。加个密码保护,至少多一层防线。
3.3.3 自签根证书
openssl req -x509 -new \
-key my-ca/private/ca.key.pem \
-out my-ca/certs/ca.cert.pem \
-days 3650 \
-config openssl.cnf \
-extensions v3_ca
这里 -days 3650 是 10 年。根证书有效期可以长一些,因为你不经常换它。但服务器证书我一般只给 1 年,到期就换,减少风险。
验证一下:
openssl x509 -in my-ca/certs/ca.cert.pem -text -noout | grep -E "Subject:|Issuer:|CA:"
如果看到 Subject 和 Issuer 相同,且 CA:TRUE,说明根证书自签成功。
3.3.4 完整的根 CA 搭建流程
我把整个流程画成了一张图,方便你对照操作:
3.4 验证你的根 CA
搭建完别急着用。先自检一下:
# 1. 检查证书基本信息
openssl x509 -in my-ca/certs/ca.cert.pem -noout -subject -issuer -dates
# 2. 检查是否为 CA 证书
openssl x509 -in my-ca/certs/ca.cert.pem -noout -purpose | grep "CA"
# 3. 自验证(根证书自己验证自己)
openssl verify -CAfile my-ca/certs/ca.cert.pem my-ca/certs/ca.cert.pem
如果第三步返回 ca.cert.pem: OK,恭喜你,根 CA 搭建成功。
ca.cert.pem 分发到所有需要信任它的设备上,然后写一个简单的测试脚本,用 openssl s_client 验证 TLS 握手是否正常。这一步能提前暴露 90% 的配置问题。
3.5 避坑指南
这些年我踩过的坑,列出来给你参考:
- 路径问题:OpenSSL 默认找
/etc/ssl/openssl.cnf,如果你用自定义配置,一定要用-config指定路径。我曾经因为忘了加这个参数,结果 OpenSSL 用了系统默认配置,颁发的证书 SAN 全丢了。 - 权限问题:私钥文件权限一定要
400或600。有一次我图省事用了644,结果被安全扫描工具报高危漏洞,被客户要求整改。 - 时间问题:证书验证依赖系统时间。如果设备时间不准,证书会报“未生效”或“已过期”。IoT 设备尤其要注意 NTP 同步。
- 序列号重复:
serial文件如果被误修改,可能导致颁发重复序列号的证书。建议每次颁发前检查一下index.txt和serial的一致性。