3. TLS/SSL 协议基础:从握手到加密,一次讲透

好,咱们进入正题。TLS 这东西,说白了就是给 MQTT 通信加一把锁。没有它,你的设备数据就是在网上裸奔。我刚开始做物联网那会儿,总觉得 TLS 配置太麻烦,结果客户现场被抓包,设备证书和密钥全被扒走……嗯,从那以后,我再也不敢偷懒了。

3.1 非对称加密 vs 对称加密:谁负责什么?

先搞清楚两个基本概念。你想想看,如果两个人要悄悄传纸条,最直接的办法是约定一个暗号(密钥),双方都用这个暗号加密解密——这就是对称加密。速度快,但问题来了:暗号怎么安全地交给对方?

非对称加密解决了这个难题。它有一对钥匙:公钥公开,私钥保密。公钥加密的数据只有私钥能解开。我在项目中见过不少新手把私钥也上传到代码仓库,那简直是给黑客送大礼。

核心分工:

  • 非对称加密(RSA/ECC):用于 TLS 握手阶段的身份认证和密钥交换。慢,但安全。
  • 对称加密(AES/ChaCha20):用于握手完成后的大量数据传输。快,但密钥必须保密。

实际 TLS 握手中,非对称加密只用来交换一个临时对称密钥,后续所有数据都用对称加密。这叫“混合加密体系”,兼顾安全与性能。

3.2 TLS 握手过程:四次挥手?不,是四次握手

我画了一张流程图,帮你把 TLS 1.2 的完整握手过程看清楚。别被吓到,其实就四个主要阶段。

TLS 1.2 完整握手流程(双向认证场景) 客户端 (MQTT Client) 服务端 (MQTT Broker) ① ClientHello(支持的TLS版本、密码套件、随机数) ② ServerHello + 服务端证书 + CertificateRequest ③ 客户端证书 + ClientKeyExchange + CertificateVerify ④ ServerFinished + 开始加密通信 双向认证 vs 单向认证 • 单向认证:只有服务端出示证书(步骤②),客户端验证服务端身份 • 双向认证:双方都出示证书(步骤②+③),互相验证身份 • MQTT 双向认证:客户端证书绑定设备身份,实现设备级认证

我挑几个关键点说说:

  • ClientHello:客户端告诉服务端“我会这些密码套件,这是我的随机数”。随机数很重要,后面生成对称密钥要用。
  • ServerHello + 证书:服务端选定密码套件,发回自己的证书。如果是双向认证,还会附带 CertificateRequest,要求客户端也出示证书。
  • 客户端证书:这一步是双向认证的关键。客户端把自己的证书发过去,还要用私钥签名一段数据(CertificateVerify),证明“我就是证书上写的那个人”。
  • Finished:双方确认握手完成,之后所有数据都用协商好的对称密钥加密。

我的经验: 调试 TLS 握手问题时,用 openssl s_client -connect host:port -debug 可以看完整握手日志。我曾经花了两天排查一个双向认证失败的问题,最后发现是客户端证书的 KeyUsage 字段缺少 digitalSignature——证书本身没问题,但服务端不认。

3.3 证书链验证:为什么需要中间 CA?

你拿到的服务器证书,通常不是根 CA 直接签发的。中间还有一层甚至多层中间 CA。为什么搞得这么复杂?

安全考虑。根 CA 的私钥如果泄露,整个信任体系就崩了。所以根 CA 的私钥通常离线保存,平时只用中间 CA 签发证书。就算中间 CA 出问题,吊销它就行,根 CA 还是安全的。

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

  1. 从服务器证书开始,逐级向上找签发者,直到信任的根 CA。
  2. 验证每级证书的签名是否有效(用上一级证书的公钥解密签名,比对证书摘要)。
  3. 检查证书是否在有效期内,是否被吊销(CRL/OCSP)。
  4. 检查证书用途(Extended Key Usage)是否匹配——比如服务器证书要有 serverAuth,客户端证书要有 clientAuth。

避坑指南: 我曾经遇到一个案例,设备上报“证书验证失败”,查了半天发现是中间 CA 证书没部署在设备信任库里。很多 IoT 设备只内置了根 CA,但服务器用的是中间 CA 签发的证书。设备必须把中间 CA 证书也装进去,否则链断了。记住:证书链必须完整,缺一环都不行。

3.4 TLS 1.2 vs TLS 1.3:到底差在哪?

TLS 1.3 是 2018 年发布的,改动很大。我直接给你对比表,一目了然。

对比项 TLS 1.2 TLS 1.3
握手轮次 2-RTT(4 次消息交换) 1-RTT(3 次消息交换,首次连接)
0-RTT 不支持 支持(恢复会话时可直接发数据)
密码套件 支持几十种,含大量老旧算法 仅支持 5 种强安全套件
密钥交换 支持 RSA、DH、ECDH 等多种 仅支持 (EC)DHE,前向安全性强制
证书加密 证书明文传输 证书加密传输(可选)
算法协商 客户端列出所有支持的,服务端选 客户端列出支持的,服务端选,但剔除了不安全算法

说白了,TLS 1.3 更快、更安全、更简单。我建议新项目直接上 TLS 1.3。但要注意,有些老旧 MQTT Broker 或设备 SDK 还不支持 1.3,这时候只能退回到 1.2。

MQTT 场景下的选择建议:

  • 如果设备性能较强(如 Linux 网关、树莓派),优先 TLS 1.3。
  • 如果设备是 ESP8266 这类资源受限的 MCU,TLS 1.2 更稳妥,因为 1.3 的握手计算量略大。
  • 双向认证在 TLS 1.2 和 1.3 中都支持,但 1.3 的握手流程略有不同,客户端证书在 EncryptedExtensions 之后发送。

3.5 密码套件选择:别用那些过时的

密码套件长这样:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256。拆开看:

  • 密钥交换:ECDHE(椭圆曲线 Diffie-Hellman 临时密钥)——提供前向安全性
  • 身份认证:RSA(用 RSA 证书签名验证身份)
  • 对称加密:AES_128_GCM(128 位 AES,GCM 模式带认证)
  • 哈希:SHA256(用于消息完整性校验)

我见过不少项目还在用 TLS_RSA_WITH_AES_128_CBC_SHA,这玩意儿既不支持前向安全,CBC 模式还有 padding oracle 攻击风险。赶紧换掉。

推荐套件(按优先级排序):

  1. TLS_AES_128_GCM_SHA256(TLS 1.3 默认)
  2. TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(ECC 证书,性能更好)
  3. TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(RSA 证书,兼容性好)

我个人习惯在服务端只开这 3 个套件,其他全关。少即是多,攻击面也小。

3.6 本章小结