密码学基础回顾:对称加密、非对称加密、哈希与消息认证码
说实话,很多做嵌入式开发的朋友一听到「密码学」三个字就头大。我当年也一样,总觉得这是数学博士才该操心的事。直到有一次,我在一个智能门锁项目里,因为选错了加密模式,导致设备被重放攻击轻松破解……嗯,从那以后我再也不敢轻视这些基础了。
这一章,咱们就把嵌入式 TLS 里最常用的几个密码学原语捋一遍。不扯复杂的数学公式,重点讲清楚「它是什么」「用在哪儿」「坑在哪儿」。
2.1 对称加密:AES 是绝对主力
对称加密,说白了就是加密和解密用同一把钥匙。速度快,适合大量数据。在 TLS 握手完成后,真正的通信数据就是用对称加密来保护的。
嵌入式领域,AES 是绝对的主流。我几乎没见过哪个现代 IoT 芯片不支持 AES 硬件加速。
AES 的关键参数
| 参数 | 常见值 | 我的建议 |
|---|---|---|
| 密钥长度 | 128 / 192 / 256 位 | 嵌入式用 AES-128 足够,省电省时间 |
| 工作模式 | ECB / CBC / GCM / CCM | 千万别用 ECB,GCM 或 CCM 最推荐 |
| IV / Nonce | 12 字节(GCM) | 每次加密必须不同,否则全完 |
我个人习惯,在 TLS 1.3 和 CoAP + DTLS 场景下,优先选 AES-128-GCM。为什么?因为它同时提供加密和完整性校验,省掉一个额外的 MAC 计算,对 MCU 的算力非常友好。
代码示例:mbedTLS 中的 AES-GCM 加解密
#include "mbedtls/gcm.h"
mbedtls_gcm_context ctx;
unsigned char key[16] = {0x00}; // 128位密钥
unsigned char iv[12] = {0x00}; // 12字节随机数
unsigned char plaintext[] = "Hello IoT";
unsigned char ciphertext[64];
unsigned char tag[16];
mbedtls_gcm_init(&ctx);
mbedtls_gcm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key, 128);
// 加密 + 生成认证标签
mbedtls_gcm_crypt_and_tag(&ctx, MBEDTLS_GCM_ENCRYPT,
sizeof(plaintext), iv, sizeof(iv),
NULL, 0, // 附加认证数据(AAD)
plaintext, ciphertext, tag, sizeof(tag));
mbedtls_gcm_free(&ctx);
你想想看,整个加密过程只需要调用一个函数,硬件加速的话几十微秒就搞定了。这就是为什么 AES-GCM 在嵌入式 TLS 里这么受欢迎。
2.2 非对称加密:RSA 与 ECC 的抉择
非对称加密,公钥加密、私钥解密。速度慢,但解决了密钥分发问题。TLS 握手阶段用它来交换对称密钥。
嵌入式设备上,RSA 和 ECC 是两大阵营。我这些年做过的项目里,ECC 正在全面取代 RSA,原因很简单——ECC 用更短的密钥提供同等安全强度。
RSA vs ECC 对比
| 对比项 | RSA | ECC |
|---|---|---|
| 密钥长度(等效安全) | 2048 位 | 256 位 |
| 签名速度 | 慢(私钥操作) | 快 |
| 验证速度 | 快(公钥操作) | 较慢 |
| 内存占用 | 大(大数运算) | 小 |
| 嵌入式适用性 | 低(尤其签名) | 高 |
我记得有个项目,用 STM32F4 做 RSA-2048 签名,一次操作耗时接近 2 秒。换成 ECC-256 后,降到 100 毫秒以内。用户体验天差地别。
椭圆曲线选择
ECC 不是随便选一条曲线就能用的。嵌入式领域,我推荐以下两条:
- secp256r1(P-256):NIST 标准,几乎所有 TLS 库都支持,硬件加速也最多。
- Curve25519(X25519):设计更安全,实现简单,没有专利问题。TLS 1.3 里是必选曲线。
我个人更倾向 X25519。为什么?它的实现几乎不可能被侧信道攻击,而且速度比 P-256 快不少。在 ESP32 上实测,X25519 密钥交换比 P-256 快 30% 以上。
2.3 哈希函数:SHA-256 是底线
哈希函数把任意长度的数据变成固定长度的摘要。不可逆,抗碰撞。在 TLS 里,它用于证书验证、密钥派生、消息完整性检查。
SHA-256 是目前嵌入式 TLS 的底线。SHA-1 已经被证明不安全,别再用了。
SHA-256 在 TLS 中的典型用途
- 证书指纹:验证证书是否被篡改。
- PRF(伪随机函数):TLS 1.2 用 SHA-256 派生会话密钥。
- Finished 消息:握手结束时的完整性校验。
- HMAC:结合密钥的消息认证码。
代码实现很简单,mbedTLS 里几行搞定:
#include "mbedtls/sha256.h"
mbedtls_sha256_context ctx;
unsigned char input[] = "data to hash";
unsigned char output[32];
mbedtls_sha256_init(&ctx);
mbedtls_sha256_starts(&ctx, 0); // 0 = SHA-256, 1 = SHA-224
mbedtls_sha256_update(&ctx, input, sizeof(input));
mbedtls_sha256_finish(&ctx, output);
mbedtls_sha256_free(&ctx);
嗯,这里要注意:很多 MCU 有硬件 SHA 加速器,记得用 mbedTLS 的硬件加速层,别傻乎乎跑软件实现。速度能差 10 倍以上。
2.4 消息认证码:HMAC 与 GCM 的认证标签
光有加密还不够。攻击者虽然解不了密,但可以篡改密文。消息认证码(MAC)就是用来检测数据是否被篡改的。
TLS 里最常用的是 HMAC(基于哈希的 MAC)。它把密钥和消息混在一起做两次哈希,安全性很高。
HMAC 的工作流程
- 将密钥与内部填充(ipad)异或,拼接消息,做第一次哈希。
- 将密钥与外部填充(opad)异或,拼接第一次哈希结果,做第二次哈希。
- 输出最终的 MAC 值。
为什么搞这么复杂?为了防止长度扩展攻击。SHA-256 本身有长度扩展漏洞,HMAC 的两次哈希结构正好堵住了这个漏洞。
不过,在 TLS 1.3 里,HMAC 被 AEAD(认证加密) 取代了。AES-GCM 和 ChaCha20-Poly1305 这类 AEAD 算法,把加密和认证合二为一,更高效、更安全。
HMAC vs AEAD 对比
| 特性 | HMAC + 加密 | AEAD(如 GCM) |
|---|---|---|
| 操作步骤 | 先加密,再 HMAC | 一步完成 |
| 性能 | 两次遍历数据 | 一次遍历 |
| 安全性 | 依赖实现顺序 | 原子操作,不易出错 |
| 嵌入式适用性 | 旧设备兼容 | 推荐新设计 |
我个人建议,新设计的嵌入式 TLS 项目,直接上 AEAD。别再用 HMAC + CBC 这种老组合了。TLS 1.3 已经强制要求 AEAD,咱们跟着标准走,准没错。
2.5 知识体系总览
下面这张图,把本章的核心知识点串起来了。你可以把它当作密码学基础的地图,后面讲到 TLS 握手时,随时回来对照。
这张图里,四个模块不是孤立的。TLS 握手时,非对称加密用来交换密钥,对称加密用来保护数据,哈希函数用来派生密钥和验证完整性,消息认证码用来防止篡改。它们配合起来,才构成了完整的 TLS 安全通道。