3. TLS/SSL加密:给MQTT穿上防弹衣

说实话,MQTT本身在传输层是裸奔的。用户名密码都是明文,设备发出去的数据谁都能抓包看到。我最早做物联网项目时,客户说“我们内网用,没事”,结果测试环境被隔壁部门抓包抓了个底朝天……从那以后,不管什么场景,TLS必须安排上。

这一章咱们就聊聊TLS的核心原理、证书体系怎么搭、单向双向认证怎么配,以及密码套件怎么选。嗯,内容不少,但都是实战干货。

一句话总结:TLS加密是MQTT安全的基石。没有TLS,你的设备就是在互联网上裸奔。

3.1 TLS协议原理:握手、加密、完整性

TLS(Transport Layer Security)说白了就是在TCP之上加了一层安全壳。它主要干三件事:

  • 身份验证——确认对方是不是它声称的那个人
  • 数据加密——传输的内容只有收发双方能看懂
  • 完整性校验——防止数据在途中被篡改

我习惯把TLS握手过程拆成四个阶段,你想想看:

  1. Client Hello:客户端说“我要连你,我支持这些密码套件”
  2. Server Hello + 证书:服务端说“好,我们用这个套件,这是我的证书”
  3. 证书验证 + 密钥交换:客户端检查证书是否可信,然后协商出一个对称密钥
  4. 加密通信开始:双方用这个密钥加密后续所有数据

这里有个关键点——非对称加密只用来握手,真正的数据传输用的是对称加密。为什么?因为对称加密快得多。我在项目中测过,同样的硬件,对称加密比非对称快两个数量级。

小提示:MQTT over TLS默认端口是8883,WebSocket over TLS是443。别搞混了,我见过有人把8883当WebSocket端口用,折腾了半天。

TLS 1.3 握手流程(简化版) MQTT客户端 MQTT Broker ① Client Hello(支持的密码套件) ② Server Hello + 证书 + 密钥交换参数 ③ 验证证书 ④ 客户端密钥交换 + 完成 ⑤ 服务端完成 ⑥ 加密通信开始(对称加密)

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配置本身不难,难的是证书生命周期管理。我建议你从一开始就把证书的签发、分发、续期、吊销流程想清楚,不然后面设备量大了,光换证书就能让你崩溃。