3. 设备唯一标识生成与注册表绑定
为什么需要“芯片级”唯一标识
在设备认证体系中,设备ID是身份的第一锚点。如果ID可以被复制、篡改或批量伪造,后续的密钥、证书、令牌都失去意义。因此本案例选择从硬件根信任出发:优先读取MCU/SoC的芯片唯一ID(UID),或从安全元件(SE/ATECC608A等)读取出厂固化序列号,再结合产品型号与批次信息,生成不可篡改的设备ID。
核心原则:设备ID必须来源于硬件不可写区域,而非软件配置或随机数生成后写入普通Flash。软件可写的ID在物理接触或固件漏洞下可被克隆。
设备ID的组成与生成规则
以STM32系列为例,96位UID位于固定系统存储区,只读且全球唯一。我们将其格式化为可读、可索引的字符串:
设备ID = "IOT-" + 产品型号 + "-" + UID_HEX(前12字节)
示例(教学假设值):
芯片UID(96bit) = 0x1FFF_3A4B_5C6D_7E8F_90A1_B2C3
产品型号 = STM32L431
设备ID = IOT-STM32L431-1FFF3A4B5C6D7E8F90A1B2C3
读取UID的参考实现(STM32 HAL):
#include "stm32l4xx_hal.h"
#include <stdio.h>
#include <string.h>
#define UID_BASE 0x1FFF7590UL /* STM32L431 UID 起始地址 */
#define UID_WORDS 3 /* 96 bit = 3 x 32 bit */
void build_device_id(char *out, size_t out_len, const char *model)
{
uint32_t uid[UID_WORDS];
for (int i = 0; i < UID_WORDS; i++) {
uid[i] = *(volatile uint32_t *)(UID_BASE + i * 4);
}
snprintf(out, out_len, "IOT-%s-%08lX%08lX%08lX",
model,
(unsigned long)uid[0],
(unsigned long)uid[1],
(unsigned long)uid[2]);
}
若使用ATECC608A,则通过I²C读取其出厂序列号(9字节),同样拼接为设备ID。关键是:读取路径必须经过硬件总线,且序列号不可被改写。
注册表绑定:设备ID ↔ 认证凭据
生成设备ID后,需要在设备注册表中建立“设备ID → 认证凭据”的绑定关系。注册表可以是本地JSON文件、SQLite数据库或云端设备管理服务。本案例以SQLite为例,表结构如下:
| 字段 | 类型 | 说明 |
|---|---|---|
| device_id | TEXT PRIMARY KEY | 硬件UID生成的唯一ID |
| credential_type | TEXT | psk / x509 / hmac |
| credential_hash | TEXT | 凭据的SHA-256哈希(不存明文) |
| status | TEXT | active / revoked / pending |
| registered_at | INTEGER | 注册时间戳 |
绑定操作示例(Python + sqlite3):
import sqlite3, hashlib, time
def register_device(db_path, device_id, credential):
cred_hash = hashlib.sha256(credential.encode()).hexdigest()
conn = sqlite3.connect(db_path)
conn.execute("""
INSERT INTO device_registry
(device_id, credential_type, credential_hash, status, registered_at)
VALUES (?, 'psk', ?, 'active', ?)
""", (device_id, cred_hash, int(time.time())))
conn.commit()
conn.close()
验证未注册设备无法通过认证
认证服务端在收到设备连接请求时,先提取设备ID,再查询注册表。若device_id不存在或status不为active,直接拒绝。验证流程如下:
- 设备上报 device_id = IOT-STM32L431-1FFF3A4B5C6D7E8F90A1B2C3
- 服务端查询注册表,命中且status=active → 继续凭据校验
- 若查询无结果 → 返回 AUTH_DENIED,断开连接
- 若status=revoked → 同样拒绝,并记录审计日志
注意:设备ID本身不是秘密,不应作为认证凭据单独使用。它只是索引键,真正的认证仍依赖PSK、证书或HMAC令牌。本案例的重点是“绑定关系”和“未注册即拒绝”。
验证方法与通过条件
完成绑定后,用以下方式验证“未注册设备无法通过认证”:
- 正向验证:使用已注册设备ID和正确PSK连接,应成功建立MQTT会话。
- 反向验证:使用一个未写入注册表的设备ID(如手动构造 IOT-STM32L431-FFFFFFFFFFFFFFFFFFFFFFFF),连接应被拒绝,服务端返回 AUTH_DENIED。
- 篡改验证:将已注册设备的UID读取地址改为普通Flash地址,重启后设备ID变化,注册表查询失败,认证被拒。
通过条件:正向连接成功,反向连接在注册表查询阶段即被拒绝,且服务端日志中记录拒绝原因。