Skip to content

端对端安全协议

FDP端对端安全协议为IoT设备提供传输层无关的安全通信能力。支持蓝牙、MQTT、WiFi Direct等多种传输方式,实现离线点对点身份认证和加密通信。

核心问题与解决方案

问题解决方案
如何确认对方身份?Ed25519签名 + Credential验证
如何验证对方权限?设备端权限表
如何防止窃听和篡改?ChaCha20-Poly1305 AEAD

设计理念:借鉴HTTPS的握手+会话模式。握手阶段交换身份凭证、协商密钥(~400字节一次性开销),通信阶段使用会话密钥加密(28字节/消息)。

算法依赖

算法用途必须使用的原因
Ed25519数字签名设备身份认证,32字节公钥作为Device ID
X25519ECDH密钥交换安全协商会话密钥,支持前向安全
HKDF-SHA256密钥派生从共享密钥派生会话密钥
ChaCha20-Poly1305AEAD加密消息加密+认证,适合无硬件加速的嵌入式设备
CBOR二进制序列化最小化消息体积,适合蓝牙低带宽场景
Base58编码Device ID可读性,无易混淆字符

实现库推荐

平台推荐库
Pythoncryptography, cbor2, base58
C/嵌入式libsodium(推荐), mbedtls, wolfssl
JavaScript@noble/ed25519, @noble/hashes

密码学基础

设备身份:Ed25519

每个设备拥有一对Ed25519密钥:

  • 私钥:32字节,设备安全存储,永不传输
  • 公钥:32字节,Base58编码后约44字符
  • Device ID = Base58(公钥)

自证明身份:公钥本身就是身份标识。能用私钥签名的,必定是该设备。

身份凭证:Credential

认证服务器签发的身份证明(永久有效):

json
{
  "uid": "user-001",
  "device_id": "5D7xB8...",
  "issued_at": 1700000000,
  "issuer_sig": "base64_ed25519_signature"
}

关键要点

  • 设备必须预置认证服务器的Ed25519公钥(issuer_pubkey)
  • 设备首次注册时获得Credential
  • 本地永久缓存
  • 握手时出示给对方验证身份

密钥交换:X25519 ECDH

双方各生成临时密钥对,交换公钥后推导共享密钥:

session_key = HKDF-SHA256(
    shared_secret = ECDH(my_ephemeral_priv, peer_ephemeral_pub),
    salt = session_id,
    info = "fd-p2p-v1"
)

前向安全:临时密钥用完即销毁。即使长期私钥泄露,历史会话仍安全。

消息加密:ChaCha20-Poly1305 AEAD

AEAD = Authenticated Encryption with Associated Data

一次操作完成:加密(机密性)+ 认证(完整性)

  • 输入:plaintext + nonce(12字节) + associated_data
  • 输出:ciphertext + 16字节Tag
  • 解密时自动验证Tag,失败则拒绝

握手协议

建立安全会话需要三步:

SessionRequest

json
{
  "type": "session_request",
  "initiator": "5D7xB8...",
  "ephemeral_pub": "base64_32bytes",
  "credential": {
    "uid": "user-001",
    "device_id": "5D7xB8...",
    "issued_at": 1700000000,
    "issuer_sig": "base64_64bytes"
  },
  "timestamp": 1700000000,
  "sig": "base64_64bytes"
}

SessionResponse

json
{
  "type": "session_response",
  "session_id": "base64_16bytes",
  "responder": "7K9mC2...",
  "ephemeral_pub": "base64_32bytes",
  "credential": {
    "uid": "device-lock-001",
    "device_id": "7K9mC2...",
    "issued_at": 1700000000,
    "issuer_sig": "base64_64bytes"
  },
  "timestamp": 1700000001,
  "sig": "base64_64bytes"
}

SessionConfirm

json
{
  "type": "session_confirm",
  "session_id": "base64_16bytes",
  "confirm_tag": "base64_16bytes"
}

confirm_tag = HMAC(session_key, "confirm" + session_id)

验证流程

  1. 验证对方的Ed25519签名(证明持有私钥)
  2. 验证对方的Credential签名(证明身份由认证服务器确认)
  3. 缓存对方的uid,用于后续查询本地权限表

消息格式

握手完成后,所有业务消息使用AEAD加密。

二进制消息结构

┌─────────────┬──────────┬─────────────────────┐
│ session_id  │   seq    │      ciphertext     │
│  (4 bytes)  │ (2 bytes)│   (变长 + 16字节Tag) │
└─────────────┴──────────┴─────────────────────┘
  • session_id[0:4]:会话标识(取前4字节)
  • seq:消息序号,uint16,防重放
  • ciphertext:AEAD加密后的数据(含16字节Tag)

Nonce管理

AEAD要求Nonce不可重复。使用 session_id[0:12] XOR seq 构造96位Nonce。

Payload格式(CBOR)

使用CBOR(二进制JSON)而非JSON,大幅减少体积:

json
{
  "a": 1,
  "t": 1700000000
}

CBOR编码后约6字节。

收发流程

发送

  1. 构造payload(action + timestamp)
  2. CBOR序列化(~6字节)
  3. 构造Nonce(session_id + seq)
  4. AEAD加密(生成密文 + Tag)
  5. 打包:session_id[0:4] + seq + ciphertext
  6. 总长度:4 + 2 + 6 + 16 = 28字节

接收

  1. 解析头部(session_id_prefix + seq)
  2. 查找会话
  3. 防重放检查(seq必须递增)
  4. AEAD解密(自动验证Tag,失败则拒绝)
  5. 解析payload
  6. 检查时间戳(5分钟窗口)
  7. 查询本地权限表
  8. 更新序号

安全性分析

威胁模型

攻击类型防御措施
身份伪造Ed25519签名。没有私钥无法通过握手验证
身份凭证伪造Credential由认证服务器签名,本地无法伪造
权限绕过权限表存储在设备端,由设备控制,通信层无法绕过
消息窃听ChaCha20加密,没有session_key无法解密
消息篡改Poly1305 Tag验证,任何修改都会导致Tag不匹配
重放攻击seq递增 + timestamp检查
中间人攻击ECDH密钥交换绑定Ed25519身份,无法插入
私钥泄露前向安全:临时ECDH密钥,历史会话不受影响

为什么不能省略Tag?

没有Tag的加密 = 没有锁的保险箱

流加密(ChaCha20)的XOR特性导致:攻击者无需密钥,只需知道消息格式,即可通过位翻转精确修改消息内容。

python
# 攻击者截获密文(不知道密钥)
ciphertext = intercept()

# 攻击者知道JSON格式,猜测明文包含 {"a":3} (查询)
# 想改成 {"a":1} (解锁)

# 直接修改密文对应位置
tampered = ciphertext
tampered[offset] ^= ord('3') ^ ord('1')  # 3→1

# 发送篡改密文 → 设备解密 → 得到 {"a":1} → 门锁打开!

有Tag保护:任何对密文的修改都会导致Tag验证失败,消息被拒绝。

性能分析

蓝牙场景

BLE默认MTU = 23字节,有效载荷 = 20字节

方案消息大小BLE包数延迟
JSON + HMAC~190字节10包10 × RTT
本协议28字节2包2 × RTT

优化效果

  • 延迟降低 80%
  • 功耗降低 80%(BLE主要功耗在传输)
  • 适合电池供电的门锁设备

MQTT场景

阶段消息大小频率
握手~400字节每次连接一次
业务消息28字节每次操作

对于MQTT,28字节的开销可忽略不计。

实现建议

预置信息

  • 认证服务器公钥(issuer_pubkey):用于验证Credential签名
  • 本地权限表:uid → permissions映射,由设备端管理

密钥存储

密钥类型存储位置生命周期
设备私钥安全元件或加密分区永久
会话密钥内存会话结束后清除
Credential本地存储永久

会话管理

  • 会话超时:30分钟无活动自动销毁
  • 最大会话数:限制同时会话数(如10个)
  • 序号溢出:seq达到65535时,重新握手

错误处理

错误处理方式
Tag验证失败立即断开,不泄露任何信息
Credential签名无效拒绝握手
权限不足返回错误码,不执行操作
私钥泄露重置设备(生成新密钥对,重新注册)

参考资料

物联网设备通信协议文档