3. PASE 协议握手流程:协议初始化、身份认证阶段、密钥协商阶段

PASE 协议的握手流程,说白了就是三个步骤:先打个招呼,再验明正身,最后商量好密钥。我刚开始接触这个协议时,觉得它跟 TLS 有点像,但实际做下来发现,它针对物联网场景做了很多简化。嗯,咱们一个一个来看。

3.1 协议初始化阶段

这个阶段的目标很简单——让发起方和响应方互相知道对方的存在,并交换一些基本参数。

我个人习惯把初始化阶段拆成两个子步骤:

  1. 设备发现:发起方(比如手机 App)广播一个 PASE 启动请求。响应方(比如智能灯泡)收到后,回复自己的设备信息。
  2. 能力协商:双方交换支持的加密套件、协议版本、最大报文长度等参数。

这里有个细节容易踩坑。我在项目中遇到过,有些设备在广播时会把 设备唯一标识(Device ID) 直接明文发出去。你想想看,这等于把家门钥匙挂在门口。正确的做法是只发一个临时标识符(Session ID),真正的身份信息留到认证阶段再处理。

初始化阶段核心交换参数

参数 说明 是否必须
协议版本号 双方协商使用的 PASE 版本 是
支持的加密套件列表 如 AES-CCM、ChaCha20-Poly1305 是
Session ID 临时会话标识,用于后续消息关联 是
设备能力标志 如是否支持 ECDH、是否支持 PAKE 否
最大报文长度 避免分片攻击 推荐

我的经验:初始化阶段不要发太多信息。我曾经见过一个方案,把设备固件版本、序列号、甚至 Wi-Fi MAC 地址都塞进广播包。结果被攻击者用来做设备指纹识别。记住,广播阶段只发必要信息。

3.2 身份认证阶段

初始化完成后,双方要证明「我是我」。PASE 协议通常使用 PAKE(Password Authenticated Key Exchange) 来做身份认证。为什么不用证书?因为物联网设备往往没有 CA 签发的证书,而且配网场景下用户输入的密码(比如 Wi-Fi 密码)本身就是共享凭证。

PAKE 的核心思想是:双方用同一个密码(通常是用户输入的配网密码)来生成认证凭证,但密码不会在网络上传输。我刚开始看 PAKE 时觉得挺绕,后来自己动手实现了一遍才明白——它本质上是在密码上叠加了一层零知识证明。

认证阶段通常包含以下消息交换:

  1. 发起方发送认证请求:包含 Session ID、PAKE 算法标识、以及一个随机数(Nonce)。
  2. 响应方回复认证挑战:包含自己的随机数、以及用密码派生的认证令牌。
  3. 发起方验证并回复:验证对方的令牌,如果正确,发送自己的认证令牌。
  4. 响应方最终确认:验证发起方的令牌,如果正确,回复认证成功。

注意:认证阶段最容易出问题的是 重放攻击。如果随机数(Nonce)生成不够随机,攻击者可以录制一次完整的认证流程,然后重放。我曾经在测试一个智能门锁时发现,它的随机数生成器用的是系统启动时间戳——结果同一秒内发起的认证,Nonce 完全一样。这等于给攻击者开了后门。

为什么会这样?很多物联网芯片的硬件随机数生成器(TRNG)质量参差不齐。我建议在软件层叠加一个 伪随机数生成器(PRNG),用硬件熵源做种子,这样即使硬件随机数不够好,也能保证 Nonce 的唯一性。

3.3 密钥协商阶段

身份认证通过后,双方要商量出一个会话密钥。PASE 协议通常使用 ECDH(椭圆曲线 Diffie-Hellman) 来做密钥协商。为什么不用 RSA?因为 ECDH 的密钥更短,计算更快,更适合资源受限的物联网设备。

密钥协商阶段的流程:

  1. 双方各自生成临时密钥对:私钥自己留着,公钥发给对方。
  2. 计算共享密钥:用自己的私钥和对方的公钥,通过 ECDH 算法计算出共享密钥。
  3. 派生会话密钥:用共享密钥加上之前交换的随机数,通过 KDF(密钥派生函数)生成最终的会话密钥。
  4. 验证密钥一致性:双方用会话密钥加密一个已知的测试向量,互相验证。

密钥派生过程示例(伪代码)

// 双方已通过 ECDH 计算出共享密钥 shared_secret
// 双方已交换随机数 nonce_initiator 和 nonce_responder

// 步骤1:拼接所有输入
input = shared_secret || nonce_initiator || nonce_responder || "PASE_SESSION_KEY"

// 步骤2:使用 HKDF 派生会话密钥
session_key = HKDF-SHA256(input, salt=0x00, info="PASE_v1", length=16)

// 步骤3:派生认证密钥(用于后续消息完整性校验)
auth_key = HKDF-SHA256(input, salt=0x01, info="PASE_v1", length=16)

这里有个关键点:临时密钥对必须是一次性的。我见过有些实现为了省事,把设备的长期密钥直接拿来用。这等于把整个设备的安全都押在一把钥匙上。正确的做法是每次握手都生成新的临时密钥对,用完就丢。

避坑指南:我曾经在调试一个智能插座时发现,它的 ECDH 实现没有做 公钥验证。攻击者可以伪造一个无效的公钥,导致计算出的共享密钥全是零。嗯,这等于把会话密钥拱手送人。所以一定要验证对方公钥是否在合法的椭圆曲线上。

3.4 完整握手流程总览

把三个阶段串起来,就是 PASE 协议的完整握手流程。我画了一张图,方便你理解整体脉络。

PASE 协议完整握手流程 发起方(Initiator) 响应方(Responder) 1. 广播 PASE 启动请求 (Session ID, 能力列表) ← 回复设备信息 → 1. 监听并回复 (支持的加密套件, 版本) 协商完成 → 初始化阶段 2. 发送认证请求 (Nonce_I, PAKE 算法) ← 验证挑战 → 发送认证令牌 → 2. 回复认证挑战 (Nonce_R, 认证令牌) 验证令牌 → ← 认证成功确认 认证阶段 3. 发送临时公钥 (PubKey_I) ← 接收公钥 → 计算共享密钥 3. 发送临时公钥 (PubKey_R) ← 接收公钥 → 计算共享密钥 密钥协商阶段 会话密钥建立完成

从图中可以看到,三个阶段是严格顺序执行的。初始化没完成,就不能进入认证;认证没通过,密钥协商就不会开始。这种设计保证了安全性——攻击者无法跳过某个阶段直接获取密钥。

3.5 常见问题与避坑

  • 超时处理:每个阶段都要设置超时。我见过一个设备,认证阶段等待对方回复时没有超时机制,结果攻击者发一个请求后就不回复了,设备一直卡在认证状态,无法响应其他请求。这就是一个简单的 DoS 攻击入口。
  • 状态机管理:PASE 握手是一个严格的状态机。发起方和响应方都要维护当前状态,收到不符合状态的消息要直接丢弃。我曾经在代码审查时发现,有个实现把「认证成功」和「密钥协商完成」两个状态搞混了,导致攻击者可以在认证成功后直接跳过密钥协商,使用一个已知的弱密钥。
  • 错误处理:任何阶段失败,都要立即终止握手,并清理所有临时数据(临时密钥、Nonce、Session ID 等)。不要给攻击者重试的机会。

重要提醒:PASE 协议的安全性建立在密码的强度上。如果用户设置的配网密码是 "123456",那再好的协议也救不了你。我建议在配网 App 端强制要求密码复杂度,至少 8 位,包含字母和数字。

好了,PASE 协议的握手流程就讲到这里。这三个阶段环环相扣,任何一个环节出问题,整个配网过程都可能被攻破。实际开发时,我建议先把状态机画清楚,再动手写代码——这样能避免很多逻辑漏洞。