3. TLS/SSL加密:给MQTT穿上防弹衣
说实话,MQTT本身在传输层是裸奔的。用户名密码都是明文,设备发出去的数据谁都能抓包看到。我最早做物联网项目时,客户说“我们内网用,没事”,结果测试环境被隔壁部门抓包抓了个底朝天……从那以后,不管什么场景,TLS必须安排上。
这一章咱们就聊聊TLS的核心原理、证书体系怎么搭、单向双向认证怎么配,以及密码套件怎么选。嗯,内容不少,但都是实战干货。
一句话总结:TLS加密是MQTT安全的基石。没有TLS,你的设备就是在互联网上裸奔。
3.1 TLS协议原理:握手、加密、完整性
TLS(Transport Layer Security)说白了就是在TCP之上加了一层安全壳。它主要干三件事:
- 身份验证——确认对方是不是它声称的那个人
- 数据加密——传输的内容只有收发双方能看懂
- 完整性校验——防止数据在途中被篡改
我习惯把TLS握手过程拆成四个阶段,你想想看:
- Client Hello:客户端说“我要连你,我支持这些密码套件”
- Server Hello + 证书:服务端说“好,我们用这个套件,这是我的证书”
- 证书验证 + 密钥交换:客户端检查证书是否可信,然后协商出一个对称密钥
- 加密通信开始:双方用这个密钥加密后续所有数据
这里有个关键点——非对称加密只用来握手,真正的数据传输用的是对称加密。为什么?因为对称加密快得多。我在项目中测过,同样的硬件,对称加密比非对称快两个数量级。
小提示:MQTT over TLS默认端口是8883,WebSocket over TLS是443。别搞混了,我见过有人把8883当WebSocket端口用,折腾了半天。
3.2 证书体系:CA与自签名
证书是TLS的身份证。你想想看,客户端怎么知道它连的Broker是真的?靠的就是证书。
3.2.1 CA证书(权威机构签发)
CA(Certificate Authority)是第三方可信机构。比如DigiCert、Let's Encrypt。你向CA申请证书,CA验证你的身份后签发证书。客户端设备里预置了CA的根证书,就能验证你的证书是否由该CA签发。
优点:全球通用,浏览器/操作系统自带信任链。
缺点:要花钱(Let's Encrypt免费但有效期短),申请流程麻烦。
3.2.2 自签名证书
自己当CA,自己给自己签发证书。我在项目中经常用这种方式——尤其是设备在内网,或者做原型验证的时候。
优点:免费,想签多少签多少,有效期自己定。
缺点:每个客户端都要手动导入自签名证书,否则会报“证书不可信”。
警告:自签名证书在生产环境要慎用。我曾经有个项目,设备端忘了更新自签名证书,结果批量设备全部连不上Broker,排查了一整天才发现是证书过期了。CA签发的证书至少有个自动续期机制。
3.2.3 生成自签名证书(实战)
用OpenSSL生成自签名证书,我习惯三步走:
# 1. 生成CA私钥和自签名根证书
openssl req -x509 -newkey rsa:2048 -keyout ca.key -out ca.crt -days 3650 -nodes
# 2. 生成服务端私钥和证书签名请求(CSR)
openssl req -newkey rsa:2048 -keyout server.key -out server.csr -nodes
# 3. 用CA根证书签名服务端证书
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365
嗯,这里要注意:-nodes参数表示私钥不加密。生产环境建议去掉这个参数,用密码保护私钥。
3.3 单向认证 vs 双向认证
这是MQTT安全里最容易搞混的概念。我画个表你就明白了:
| 认证方式 | 谁验证谁 | 客户端需要证书吗 | 安全等级 | 典型场景 |
|---|---|---|---|---|
| 单向认证 | 客户端验证服务端 | 不需要 | 中 | 普通IoT设备连接云平台 |
| 双向认证 | 互相验证 | 需要 | 高 | 金融、车联网、工业控制 |
3.3.1 单向认证配置
单向认证就是客户端拿着CA证书去验证Broker。配置起来很简单:
Broker端(Mosquitto示例):
listener 8883
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
tls_version tlsv1.2
require_certificate false
客户端端(Python Paho示例):
import paho.mqtt.client as mqtt
client = mqtt.Client()
client.tls_set(ca_certs="ca.crt",
certfile=None,
keyfile=None)
client.connect("broker.example.com", 8883, 60)
注意certfile和keyfile传了None——这就是单向认证的标志。
3.3.2 双向认证配置
双向认证要求客户端也有自己的证书。Broker验证客户端证书是否由可信CA签发。
Broker端:
listener 8883
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
tls_version tlsv1.2
require_certificate true
看到区别了吗?require_certificate true——这就是开关。
客户端端:
client = mqtt.Client()
client.tls_set(ca_certs="ca.crt",
certfile="client.crt",
keyfile="client.key")
client.connect("broker.example.com", 8883, 60)
我的经验:双向认证虽然安全,但证书管理成本高。每个设备都要烧录唯一的证书,设备坏了换新设备还得重新签发。我建议只在安全要求极高的场景用双向认证,普通场景单向认证+强密码+ACL就够了。
3.4 密码套件选择
密码套件(Cipher Suite)是一组算法的组合。它决定了:
- 密钥交换算法(如ECDHE、RSA)
- 身份验证算法(如RSA、ECDSA)
- 对称加密算法(如AES、CHACHA20)
- 消息认证码算法(如SHA256、POLY1305)
我见过不少项目,密码套件选错了导致性能崩盘。举个例子,有些低端MCU没有硬件AES加速,用AES-256-GCM反而比CHACHA20-POLY1305慢得多。
3.4.1 推荐密码套件
| 套件名称 | 密钥交换 | 加密 | MAC | 推荐场景 |
|---|---|---|---|---|
| TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 | ECDHE | AES-128-GCM | SHA256 | 通用推荐,性能均衡 |
| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 | ECDHE | AES-256-GCM | SHA384 | 高安全场景 |
| TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 | ECDHE | CHACHA20 | POLY1305 | 无AES硬件加速的设备 |
3.4.2 密码套件配置示例
在Mosquitto中限制密码套件:
listener 8883
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
tls_version tlsv1.2
ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384
避坑指南:我曾经在项目里用了tls_version tlsv1.0,结果安全审计没过。现在TLS 1.0和1.1已经被RFC 8996废弃了,最低也要用TLS 1.2。有条件直接上TLS 1.3,握手快了一倍。
3.5 本章小结
嗯,这一章内容确实不少。我帮你捋一下重点:
- TLS握手分四步,核心是身份验证+密钥协商
- CA证书用于生产,自签名证书适合测试和内网
- 单向认证只验证服务端,双向认证互相验证
- 密码套件要根据硬件能力选,别盲目追求高强度
说实话,TLS配置本身不难,难的是证书生命周期管理。我建议你从一开始就把证书的签发、分发、续期、吊销流程想清楚,不然后面设备量大了,光换证书就能让你崩溃。