3、对称加密算法(AES)
对称加密,说白了就是加密和解密用同一把钥匙。你想想看,就像你家大门,用一把钥匙锁上,也用同一把钥匙打开。AES 是目前最主流的对称加密算法,没有之一。我在嵌入式项目里几乎天天跟它打交道,从简单的传感器数据加密,到关键的固件升级包校验,都离不开它。
3.1 AES算法原理:到底在折腾什么?
AES 的全称是 Advanced Encryption Standard,高级加密标准。它本质上是一个分组密码,什么意思呢?就是把明文切成一块一块,每块 128 位(16字节),然后一块一块地加密。
它的核心操作其实就四个字:混淆和扩散。怎么做到的呢?通过四步操作:
- SubBytes(字节代换):用一个固定的 S 盒,把每个字节替换成另一个字节。这步是非线性的,主要提供混淆。
- ShiftRows(行移位):把状态矩阵的每一行循环左移不同的位数。这步开始扩散。
- MixColumns(列混合):对每一列进行数学变换,让一个字节的变化影响到整列。扩散的主力。
- AddRoundKey(轮密钥加):把当前状态和本轮的子密钥做异或。这是密钥介入的地方。
嗯,这里要注意,最后一轮会省略 MixColumns。为什么?我记得当年看标准文档时也纳闷,后来才明白,这是为了加解密结构对称,方便硬件实现。
核心要点:AES 的安全性建立在「代换-置换网络」上。每一轮都在做「替换-移位-混合-加密钥」这套组合拳。轮数越多,越安全,但也越慢。
3.2 ECB/CBC/CTR模式:别只用ECB!
AES 本身只定义了怎么加密一个 128 位的数据块。但实际数据往往很长,怎么处理多块数据?这就引出了工作模式。
3.2.1 ECB模式(电子密码本模式)
最简单粗暴。每块数据独立加密,互不影响。
// 伪代码示意
for each block in plaintext:
ciphertext_block = AES_encrypt(block, key)
优点:可以并行计算,速度快。某个块出错不影响其他块。
致命缺点:相同的明文块会得到相同的密文块。我在项目中遇到过有人用 ECB 加密图片,结果加密后的图片还能看出轮廓,这就是典型的「信息泄露」。千万不要在正式产品里用 ECB,除非你只是做测试。
3.2.2 CBC模式(密码分组链接模式)
每个明文块先和前一个密文块异或,再加密。第一个块需要一个初始向量 IV。
// 伪代码示意
ciphertext[0] = AES_encrypt(plaintext[0] XOR IV, key)
for i = 1 to n:
ciphertext[i] = AES_encrypt(plaintext[i] XOR ciphertext[i-1], key)
这是我个人最常用的模式。它解决了 ECB 的「相同明文相同密文」问题。但有个坑:加密不能并行,因为每个块依赖前一个块。不过解密可以并行,因为解密时只需要前一个密文块。
避坑指南:我曾经在做一个物联网网关项目时,IV 用了固定值 0x00...00。结果安全审计直接被亮红灯。IV 必须每次加密都随机生成,并且不能重复使用。记住:IV 不需要保密,但必须唯一且不可预测。
3.2.3 CTR模式(计数器模式)
把计数器加密后的结果和明文异或,得到密文。计数器每次递增。
// 伪代码示意
counter = nonce || 0x00000001
for each block in plaintext:
keystream = AES_encrypt(counter, key)
ciphertext_block = plaintext_block XOR keystream
counter++
CTR 模式有个巨大的好处:加解密完全一样,都是加密计数器然后异或。这在硬件上可以复用同一个模块。而且可以并行,随机访问(想解密第 N 块,直接算第 N 个计数器的值就行)。
我建议在资源受限的 MCU 上优先考虑 CTR 模式。但要注意:计数器绝对不能重复,否则两个密文块异或就能消掉密钥流,直接暴露明文差异。
3.3 AES-128/256实现:选哪个?
AES 有三种密钥长度:128、192、256 位。对应轮数分别是 10、12、14 轮。
| 版本 | 密钥长度 | 轮数 | 安全性 | 速度(相对) |
|---|---|---|---|---|
| AES-128 | 128 位 | 10 | 足够(除非量子计算) | 最快 |
| AES-192 | 192 位 | 12 | 较高 | 中等 |
| AES-256 | 256 位 | 14 | 最高 | 最慢 |
我个人习惯:普通嵌入式产品用 AES-128 就够了。为什么?因为目前没有任何实际可行的攻击能破解 AES-128。AES-256 多出来的 4 轮运算,在低端 MCU 上可能要多花 30%-40% 的时间。除非你做的是金融级安全或者要对抗国家级对手,否则 AES-128 是性价比最高的选择。
但要注意:密钥存储比密钥长度更重要。我曾经见过一个产品,用了 AES-256,但密钥直接硬编码在 Flash 里明文存储。这就像给保险箱装了最贵的锁,却把钥匙贴在门上。
3.4 硬件加速器配置:别让CPU干苦力
在 MCU 上纯软件跑 AES,一个 128 位块大概需要几百到几千个 CPU 周期。如果数据量大,CPU 就被吃死了。所以现代 MCU 基本都集成了 AES 硬件加速器。
以 STM32 为例,它的 AES 外设配置大致如下:
// 伪代码:STM32 AES 硬件加速器配置
void AES_Init(void) {
// 1. 使能时钟
RCC->AHBENR |= RCC_AHBENR_AESEN;
// 2. 配置模式:CBC 加密,128位密钥
AES->CR = AES_CR_MODE_ENC // 加密模式
| AES_CR_CHMOD_1 // CBC 模式
| AES_CR_DATATYPE_1; // 32位数据对齐
// 3. 写入密钥(从安全存储区读取)
AES->KEYR0 = key[0];
AES->KEYR1 = key[1];
AES->KEYR2 = key[2];
AES->KEYR3 = key[3];
// 4. 写入 IV(CBC模式需要)
AES->IVR0 = iv[0];
AES->IVR1 = iv[1];
AES->IVR2 = iv[2];
AES->IVR3 = iv[3];
// 5. 使能 DMA 或中断(可选)
AES->CR |= AES_CR_DMAEN;
}
实战技巧:使用硬件加速器时,记得检查数据对齐。很多硬件要求输入输出缓冲区 4 字节对齐。我踩过这个坑:没对齐时,硬件会触发总线错误,或者默默返回错误结果。另外,密钥寄存器在写入后应立即清零,防止被调试器读出。
硬件加速器的好处是:几乎不占 CPU 时间。你只需要把数据喂给外设,然后去干别的事,等中断或 DMA 完成通知你就行。在低功耗场景下,这能省下大量电量。
3.5 知识体系总览
下面这张图是我自己整理的 AES 知识结构,帮你把今天讲的内容串起来:
这张图把 AES 拆成了三个维度:算法原理、工作模式、实现方式。你从任何一个维度切入都能学,但我建议先搞懂原理,再选模式,最后落地实现。
最后说一句:对称加密是嵌入式安全的基石。但记住,算法本身很少出问题,出问题的往往是密钥管理、随机数生成、模式选择这些「外围」环节。我见过太多「AES-256 加密被破解」的案例,最后查出来都是 IV 重用或者密钥硬编码。把基础打牢,比追求高版本更重要。