4、轻量级证书方案:CWT/CBOR Web Token与最小证书设计

好,咱们进入第四章。这一章聊的是「轻量级」。在IoT里,重量级的东西往往活不下来——你想想看,一个传感器节点,RAM才几十KB,你让它去解析一个几KB的X.509证书,再算一次RSA-2048签名,那基本就卡死了。所以,我们必须找到更轻的路。

我个人习惯把这一章的内容归纳为「三个替换」:用CBOR替换JSON,用CWT替换JWT,用Ed25519替换RSA/ECDSA。下面我们一个一个拆开讲。

4.1 CWT/CBOR Web Token:比JWT瘦一圈

先说说CWT。全称是CBOR Web Token,标准在RFC 8392。它和JWT的关系,说白了就是「同一套逻辑,换了个更紧凑的编码」。JWT用JSON,CWT用CBOR。

CBOR是什么?它是一种二进制序列化格式,类似JSON但更小、更快。我做过一个对比实验:同样的payload,JWT大概250字节,CWT只有160字节左右。在LoRa或者NB-IoT这种MTU只有几百字节的链路上,这90字节的差距可能就是「能发」和「发不了」的区别。

CWT的结构和JWT几乎一样:

  • 头部(Protected Header):算法标识、密钥ID等
  • 载荷(Payload):iss、sub、aud、exp等声明
  • 签名(Signature):MAC或数字签名

但CWT用CBOR的整数键映射代替了JSON的字符串键。比如:

// JWT 中的 "iss" 字段
{"iss": "device-001"}

// CWT 中对应的 CBOR 编码
A1 01 6A 64 65 76 69 63 65 2D 30 30 31
// 键 0x01 代表 "iss",值用文本字符串编码

你看,光是键名就从3个字节("iss")变成了1个字节(0x01)。积少成多,整个token就瘦下来了。

我的经验: 在项目中如果要用CWT,建议先确认你的MCU有没有CBOR硬件加速器。没有的话,纯软件解析CBOR也不慢,但要注意栈空间——CBOR的嵌套解析需要递归,栈小的平台要小心。

4.2 嵌入式证书格式对比:谁更合适?

接下来,我们对比一下几种常见的嵌入式证书格式。我列了个表,方便你一眼看清差异。

格式 编码方式 典型大小 解析复杂度 适用场景
X.509 DER ASN.1 DER 500-2000 B 高(需要ASN.1解析器) 网关、服务器
X.509 PEM Base64 + DER 700-2800 B 高 调试、配置
CWT CBOR 100-300 B 低 传感器、终端节点
Raw Public Key 裸公钥 32-64 B 极低 受限节点、静态配置
Mini-Cert 自定义二进制 50-150 B 自定义 极受限节点

从这张表能看出来,X.509 DER虽然通用,但在IoT终端上太重了。我见过一个项目,工程师硬把X.509解析器塞进Cortex-M0,结果Flash占了一半,跑起来还动不动就栈溢出。后来换成CWT,问题全解决了。

Raw Public Key(RPK)是最轻的,只有公钥本身。但它没有元数据——没有过期时间、没有颁发者、没有密钥用途。所以RPK只适合在封闭网络里用,比如工厂内部传感器网络。

注意: 不要为了追求「小」而牺牲安全性。我曾经见过有人把RSA-1024的裸公钥直接硬编码在固件里,结果密钥泄露后整个产品线都得召回。轻量级不等于「裸奔」。

4.3 Ed25519曲线在IoT中的优势

说到轻量级签名,就绕不开Ed25519。这条曲线是Daniel J. Bernstein设计的,属于Curve25519家族。我最早接触它是在做安全固件更新的时候,当时被RSA-2048的签名速度折磨得不行——一个签名验证要几百毫秒,用户等得直骂娘。

换成Ed25519之后,验证时间降到了几毫秒。为什么这么快?因为Ed25519用了扭曲爱德华兹曲线,计算上比Weierstrass曲线(比如P-256)更高效。具体来说:

  • 签名大小:64字节(32字节R + 32字节S)
  • 公钥大小:32字节
  • 私钥大小:32字节
  • 验证速度:比ECDSA P-256快2-3倍
  • 抗侧信道攻击:设计上天然免疫定时攻击

我拿一个实际项目的数据说话:在STM32F103(72MHz Cortex-M3)上,Ed25519签名验证平均耗时2.1ms,而ECDSA P-256需要5.8ms,RSA-2048更是高达28ms。这差距,在电池供电的设备上就是「一年换一次电池」和「三个月换一次电池」的区别。

还有一个容易被忽略的点:Ed25519的私钥生成不需要高质量随机数。因为它的私钥是直接随机生成的32字节,然后通过哈希派生公钥。而ECDSA的私钥生成需要检查是否在曲线阶的范围内,有时候会重试。在缺乏硬件随机数发生器的低成本MCU上,这个差异很要命。

核心结论: 如果你的IoT设备CPU主频低于100MHz、RAM小于64KB、Flash小于256KB,Ed25519几乎是唯一现实的选择。RSA和ECDSA在这个量级上要么太慢,要么太大。

4.4 最小证书设计原则

好了,前面讲了工具,现在讲方法论。最小证书设计,说白了就是「能砍的都砍掉,只留必须的」。我总结了五条原则,都是踩坑踩出来的。

  1. 原则一:只保留认证路径必需的字段

    X.509证书里有一堆扩展字段:密钥用法、增强型密钥用法、证书策略、颁发者备用名……在IoT里,大部分都用不上。CWT的设计思路就是只保留iss、sub、exp、iat这几个核心字段,其他的一律砍掉。

  2. 原则二:用整数键代替字符串键

    CBOR的整数键映射比JSON的字符串键省很多空间。比如CWT里,iss的键是0x01,sub是0x02,exp是0x04。一个字节搞定。

  3. 原则三:固定长度字段用二进制编码

    时间戳用Unix时间戳(4字节或8字节整数),不要用字符串。公钥直接用原始字节,不要Base64。我见过有人把32字节的公钥用Base64编码成44字节,白白浪费12字节。

  4. 原则四:签名和公钥用同一曲线

    不要混用。比如签名用Ed25519,公钥用RSA,那证书里就得同时存两种密钥材料,体积翻倍。统一用Ed25519,公钥32字节,签名64字节,加起来不到100字节。

  5. 原则五:考虑证书链的聚合

    如果设备需要验证整个证书链(根CA→中间CA→设备证书),可以把链上的所有证书打包成一个CBOR数组。这样设备只需要一次解析,就能拿到所有公钥和签名。我在一个项目中用这个办法,把证书链从1.2KB压缩到了380字节。

下面这张图展示了最小证书的设计流程,从原始X.509到最终CWT的裁剪过程:

最小证书设计流程:从X.509到CWT 原始X.509证书 ~800-2000字节 裁剪非必要字段 保留iss/sub/exp/iat CBOR编码 整数键 + 二进制值 Ed25519签名 64字节签名 最终CWT证书 ~100-300字节 裁剪要点 • 去掉密钥用途扩展 • 去掉颁发者备用名 • 去掉证书策略 • 时间戳用Unix整数 • 公钥用原始字节

嗯,这张图把整个流程串起来了。从左边可以看到我们砍掉了哪些东西,右边是每一步的产出。最终CWT证书的大小只有原始X.509的十分之一左右。

避坑指南: 我曾经在一个项目里把证书砍得太狠,连exp(过期时间)都砍掉了。结果设备上线后,证书永不过期,密钥泄露了也没法吊销。后来不得不远程升级固件,把exp加回来。所以,「最小」不等于「残缺」,安全底线不能碰。

最后说一句:轻量级证书不是「偷工减料」,而是「精准设计」。你砍掉的每一个字段,都要问自己一句:「这个字段在设备生命周期内真的需要吗?」如果答案是「不一定」,那就砍掉。如果答案是「可能用得上」,那就保留。IoT的世界里,每一字节都珍贵。