密码学基础回顾:对称加密、非对称加密、哈希与消息认证码

说实话,很多做嵌入式开发的朋友一听到「密码学」三个字就头大。我当年也一样,总觉得这是数学博士才该操心的事。直到有一次,我在一个智能门锁项目里,因为选错了加密模式,导致设备被重放攻击轻松破解……嗯,从那以后我再也不敢轻视这些基础了。

这一章,咱们就把嵌入式 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) 每次加密必须不同,否则全完
我曾经踩过的坑:ECB 模式加密图片数据,结果密文里还能看出图片轮廓。因为 ECB 对相同明文块输出相同密文块,完全暴露了数据模式。嵌入式设备里,只要数据有重复结构(比如传感器读数),ECB 就是灾难。

我个人习惯,在 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 毫秒以内。用户体验天差地别。

实用建议:如果设备需要做签名(比如固件升级验证),优先选 ECDSA。如果只是做密钥交换,ECDHE 是 TLS 1.3 的标配。RSA 现在基本只用于兼容旧设备。

椭圆曲线选择

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 已经被证明不安全,别再用了。

我曾经见过:有厂商为了省 Flash,在固件签名里用 MD5。结果攻击者轻松伪造了签名,远程植入了恶意固件。省那几百字节,最后赔了几百万。

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 的工作流程

  1. 将密钥与内部填充(ipad)异或,拼接消息,做第一次哈希。
  2. 将密钥与外部填充(opad)异或,拼接第一次哈希结果,做第二次哈希。
  3. 输出最终的 MAC 值。

为什么搞这么复杂?为了防止长度扩展攻击。SHA-256 本身有长度扩展漏洞,HMAC 的两次哈希结构正好堵住了这个漏洞。

关键区别:哈希函数只保证「数据没变」,HMAC 保证「数据没变 + 确实是合法发送者发的」。在 TLS 里,HMAC 用于 Finished 消息和记录层保护。

不过,在 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 握手时,随时回来对照。

密码学基础 · 知识体系总览 对称加密 (AES) AES-128 / 256 GCM / CCM 模式 非对称加密 (RSA/ECC) RSA-2048(兼容) ECC P-256 / X25519 哈希函数 (SHA-256) 证书指纹 密钥派生 (PRF) 消息认证码 (MAC) HMAC-SHA256 AEAD (GCM/Poly1305) TLS 1.3 强制使用 AEAD,推荐 AES-128-GCM + ECDHE (X25519) + SHA-256 嵌入式选型核心:安全强度够用 + 硬件加速支持 + 功耗可控

这张图里,四个模块不是孤立的。TLS 握手时,非对称加密用来交换密钥,对称加密用来保护数据,哈希函数用来派生密钥和验证完整性,消息认证码用来防止篡改。它们配合起来,才构成了完整的 TLS 安全通道。

一句话总结:嵌入式 TLS 的密码学选型,记住「AES-128-GCM + ECDHE (X25519) + SHA-256」这个组合,能覆盖 90% 以上的 IoT 场景。剩下的 10%,要么是资源极度受限(比如 8 位 MCU),要么是合规要求特殊(比如国密 SM 系列)。