AES-128 加密与解密详解
AES-128 加密与解密详解
AES (Advanced Encryption Standard) 是一种对称加密算法,用于保护敏感数据。AES-128 特指使用 128 位密钥长度的 AES 实现。
基本概念
- 密钥长度:AES-128 使用 128 位(16 字节)的密钥
- 块大小:AES 是分组密码,固定处理 128 位(16 字节)的数据块
- 轮数:AES-128 包含 10 轮转换
- 状态矩阵:AES 将 16 字节数据组织为 4×4 字节矩阵
AES-128 加密过程
AES-128 加密过程包含以下步骤:
-
密钥扩展:从原始 128 位密钥生成 11 个轮密钥(每轮一个,加上初始轮)
- 原始128位密钥被视为前4个32位字(W[0]到W[3])
- 通过迭代计算过程生成总共44个字(W[0]到W[43])
- 每4个字组成一个轮密钥(128位)
- 密钥扩展算法:
对于i从4到43:如果(i % 4 == 0):W[i] = W[i-4] ⊕ SubWord(RotWord(W[i-1])) ⊕ Rcon[i/4]否则:W[i] = W[i-4] ⊕ W[i-1]
- SubWord:对4字节进行S盒替换
- RotWord:字节循环左移
- Rcon:轮常量,引入非线性
-
初始轮:将原始数据与第一个轮密钥进行异或操作
- 这是一个简单的字节级异或操作
- 如果明文块为M,初始轮密钥为K₀,则运算为:状态 = M ⊕ K₀
- 该操作使得加密从一开始就依赖于密钥
-
9 个标准轮:每个标准轮包含四个步骤:
-
SubBytes:使用 S 盒(替换表)将每个字节替换为另一个字节
- 每个字节被视为GF(2⁸)上的元素
- 先计算乘法逆元(除了0映射到自身)
- 再进行仿射变换:b’ᵢ = b’ᵢ ⊕ b’(ᵢ+4)mod8 ⊕ b’(ᵢ+5)mod8 ⊕ b’(ᵢ+6)mod8 ⊕ b’(ᵢ+7)mod8 ⊕ c
- S盒是预先计算好的256字节查找表,提供非线性保护
- 防御线性密码分析和差分密码分析
-
ShiftRows:对状态矩阵的行进行循环左移
- 第0行不移动
- 第1行左移1字节:[r1c0, r1c1, r1c2, r1c3] → [r1c1, r1c2, r1c3, r1c0]
- 第2行左移2字节:[r2c0, r2c1, r2c2, r2c3] → [r2c2, r2c3, r2c0, r2c1]
- 第3行左移3字节:[r3c0, r3c1, r3c2, r3c3] → [r3c3, r3c0, r3c1, r3c2]
- 此步骤提供行间扩散,确保列间的数据混合
-
MixColumns:对状态矩阵的列进行混合操作
- 将每列视为GF(2⁸)上的多项式
- 与固定多项式c(x) = 3x³ + x² + x + 2相乘
- 具体矩阵运算:
[ 2 3 1 1 ] [ s₀,c ] [ s'₀,c ] [ 1 2 3 1 ] × [ s₁,c ] = [ s'₁,c ] [ 1 1 2 3 ] [ s₂,c ] [ s'₂,c ] [ 3 1 1 2 ] [ s₃,c ] [ s'₃,c ]
- 例如:s’₀,c = (2·s₀,c) ⊕ (3·s₁,c) ⊕ s₂,c ⊕ s₃,c
- 运算在GF(2⁸)上进行,乘法是多项式乘法模x⁸+x⁴+x³+x+1
- 此步骤为算法提供列间高扩散性
-
AddRoundKey:将当前轮密钥与状态矩阵进行异或
- 简单字节级异或操作:状态 = 状态 ⊕ 轮密钥[轮数]
- 每轮使用不同的轮密钥,增加密码强度
- 为算法引入密钥依赖性
-
-
最终轮:只执行 SubBytes、ShiftRows 和 AddRoundKey,不包含 MixColumns
- 省略MixColumns操作可以使加密和解密结构更对称
- 不影响安全性,因为之前9轮已提供足够扩散
- 最后轮密钥使状态变为最终密文输出
AES-128 解密过程
AES-128 解密本质上是加密的逆过程:
- 密钥扩展:同加密过程,生成相同的轮密钥
- 初始轮:将密文与最后一个轮密钥进行异或操作
- 9 个标准轮:每个标准轮包含四个反向步骤:
- InvShiftRows:对状态矩阵的行进行循环右移
- InvSubBytes:使用逆 S 盒将每个字节替换回原字节
- AddRoundKey:将当前轮密钥与状态矩阵进行异或
- InvMixColumns:对状态矩阵的列进行逆混合操作
- 最终轮:只执行 InvShiftRows、InvSubBytes 和 AddRoundKey(与第一个轮密钥)
CBC 模式工作原理
在代码中使用的是 AES-CBC 模式(Cipher Block Chaining):
- 加密过程:
- 第一个明文块与初始向量 (IV) 进行异或,然后使用 AES 加密
- 后续明文块与前一个密文块异或后再加密
- 每个密文块都依赖于前面所有的明文块
IV ↓⊕ → AES-Encrypt → 密文块1↗ ↓
明文块1 ⊕ → AES-Encrypt → 密文块2↗ ↓明文块2 ...
- 解密过程:
- 对密文块使用 AES 解密,然后与初始向量或前一个密文块异或得到明文
- 第一个密文块解密后与 IV 异或得到第一个明文块
- 后续密文块解密后与前一密文块异或得到相应的明文块
密文块1 → AES-Decrypt → ⊕ → 明文块1↓ ↑密文块2 → AES-Decrypt → ⊕ → 明文块2↓ ↑... IV
AES-CBC 初始向量(IV)的重要性
在 AES-CBC 模式中,初始向量(IV)扮演着至关重要的角色,它不是可选项,而是安全必需品。以下是初始向量存在的几个关键原因:
1. 打破加密的确定性
如果没有初始向量,相同的明文使用相同密钥加密总会产生相同的密文。这会带来严重安全隐患:
- 攻击者可以发现重复模式,推断出明文内容的相似性
- 相同数据的加密结果相同,可能泄露信息模式,尤其是在结构化数据中
初始向量通过与第一个明文块异或操作,引入随机性,即使相同明文和密钥也会产生完全不同的密文。
2. 防止字典攻击
没有IV的CBC模式会使预计算攻击变得可行。攻击者可以构建常见明文块到密文块的映射表(即"字典")。有了IV,即使是预先知道的明文,也会因IV的不同而产生不同密文,使此类攻击无效。
3. 保证语义安全
IV确保了即使加密相同的数据多次,每次产生的密文也完全不同,这是语义安全的基本要求之一。这防止了攻击者从多次加密中推断出任何有关明文的信息。
4. 中和CBC链式特性的首块问题
CBC模式的特点是每个明文块与前一个密文块异或后再加密,但第一个明文块没有"前一个密文块"。IV恰好解决了这个问题,为第一块提供了异或操作所需的输入值。
5. IV的选择原则
IV的使用有几个关键原则:
- 唯一性:每次加密操作应使用不同的IV
- 不可预测性:对于需要高安全性的应用,IV应该是随机生成的
- 公开性:IV一般不需要保密,通常与密文一起存储或传输
- 固定长度:IV长度必须与加密块大小相同(AES为16字节)
6. 不同应用场景中的IV处理
-
通信协议:
- IV通常作为消息头的一部分发送
- 每个会话或每条消息使用不同IV
-
文件加密:
- IV常存储在文件头部
- 每个文件使用不同IV
-
固件加密:
- 在可信固件更新中,IV可以是:
- 完全随机生成(最安全)
- 基于版本号派生(便于版本控制)
- 基于时间戳派生
- IV需要与加密固件一起传输到设备
- 在可信固件更新中,IV可以是:
7. IV与安全问题实例
如果两次加密使用相同的IV和密钥,会出现严重安全问题:
假设有两个明文块M₁和M₂,使用相同的(Key,IV)加密:
- C₁ = E(M₁ ⊕ IV, Key)
- C₂ = E(M₂ ⊕ IV, Key)
如果攻击者知道M₁的内容,他们可以推导出:
- M₁ ⊕ M₂ = D(C₁, Key) ⊕ D(C₂, Key)
这种"两次使用攻击"(Two-time pad attack)可能导致信息泄露。
在可信固件更新系统中,即使每次使用相同的密钥加密固件,通过使用不同的IV,也能确保每次生成不同的密文,大大增强了系统的安全性。
代码中的实现
在 IAP_AES_CBC_Decrypt
函数中,AES-CBC 解密是这样实现的:
uint32_t IAP_AES_CBC_Decrypt(const uint8_t *pCiphertext, uint32_t len, const uint8_t *key, const uint8_t *iv, uint8_t *pPlaintext)
{mbedtls_aes_context aes_ctx;unsigned char iv_copy[16]; // 复制IV,因为解密过程会修改IVint ret;// 确保长度是16字节对齐的if (len % 16 != 0)return 1; // 错误:长度必须是16字节的倍数// 初始化AES上下文mbedtls_aes_init(&aes_ctx);// 设置解密密钥 (AES-128是16字节密钥,128位)ret = mbedtls_aes_setkey_dec(&aes_ctx, key, 128);if (ret != 0)return ret;// 复制IV值,因为mbedtls_aes_crypt_cbc会修改iv参数memcpy(iv_copy, iv, 16);// 执行CBC模式解密ret = mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, len, iv_copy, pCiphertext, pPlaintext);// 释放上下文mbedtls_aes_free(&aes_ctx);return ret;
}
关键步骤解释:
- 创建AES上下文:
mbedtls_aes_init(&aes_ctx)
初始化一个AES计算环境 - 设置解密密钥:
mbedtls_aes_setkey_dec
函数处理密钥扩展,为解密做准备 - IV处理:复制初始向量,因为解密过程会修改它
- CBC解密:
mbedtls_aes_crypt_cbc
执行完整的CBC模式解密- 处理每个16字节的块
- 解密每个块后与前一个密文块或IV异或
- 将结果输出到明文缓冲区
在固件更新流程中的应用
在固件更新过程中,AES-CBC解密用于确保固件传输的安全性:
- 固件在上位机加密并保存到外部Flash
- Bootloader读取加密的固件
- 使用预设密钥和IV进行解密
- 验证解密后固件的SHA-256哈希值
- 写入内部Flash
特别注意的是,代码中正确地处理了IV链,确保每个块都使用正确的IV:
// 保存下一个块的IV (当前块的最后16字节密文)
if(remaining > block_size) { // 如果不是最后一块uint8_t next_iv[16];W25Q128_read(next_iv, APP_SAVE_ADDR + offset + block_size - 16, 16);memcpy(current_iv, next_iv, 16);
}
这确保了能够分块处理大型固件,而不需要一次性将整个固件加载到内存中,这是嵌入式系统中的常见实践。
安全性考虑
- 密钥管理:AES-128密钥需要安全存储,避免被提取
- IV唯一性:对于每次加密,IV应该是唯一的,以防止重放攻击
- 数据完整性:AES只提供保密性,不提供完整性验证,因此需要配合SHA-256等哈希算法
- 填充安全:PKCS#7填充需要正确实现以避免填充甲类攻击
代码通过结合AES-CBC加密和SHA-256哈希验证,提供了相对完善的固件安全保护机制。