
简介本资源是一份面向C开发者与密码学初学者的AES-128加密算法原理详解文档聚焦对称分组密码的核心机制与工程实现要点。文档系统梳理了AES的轮函数结构SubBytes、ShiftRows、MixColumns、AddRoundKey、密钥扩展流程、S盒查表原理并结合AVR汇编示例说明关键运算的底层实现逻辑帮助读者深入理解算法设计思想与实际编码映射关系。资源为单个110KB的DOC格式文档内容完整覆盖算法原理、与DES的对比分析、轮数设定依据及典型应用场景适合作为密码学课程补充材料或嵌入式加密模块开发参考。目前已有1680人学习下载虽未提供可运行代码但其清晰的图文推演与分步解析为后续C等语言的自主实现奠定了扎实的理论基础。1. AES128位加密过程详解不是“套个库就完事”的黑匣子而是16字节状态矩阵上10轮精密手术的实操拆解你手头有个嵌入式设备要传敏感配置或者在写一个轻量级通信协议需要自己把明文变成不可读密文——这时候搜“AES128”十有八九跳出来的是 OpenSSL 的EVP_EncryptInit_ex调用示例或者 Python 的pycryptodome一行cipher.encrypt()。但真当你在 AVR 单片机上跑不通、在裸机 Bootloader 里加解密失败、或调试时发现密文和标准向量对不上就会意识到AES 不是 API 封装层下的魔法它是一套严格定义在 4×4 字节状态矩阵State上的、可逐轮手算验证的确定性变换流程。本文不讲“为什么安全”只聚焦“怎么走通”以 128 位密钥、128 位明文为唯一输入从初始AddRoundKey开始完整复现全部 10 轮SubBytes → ShiftRows → MixColumns → AddRoundKey第10轮省略MixColumns并给出每一步在内存中真实字节位置的变化、S盒查表逻辑、GF(2⁸)乘法的手动推演、以及密钥扩展表 w[] 的逐行生成过程。这不是理论推导而是你能在 Keil、IAR 或 GDB 里单步跟踪、在纸上演算、在寄存器窗口里亲眼看到State[0][0]如何从0x00变成0x69的完整链路。适合正在移植 AES 到资源受限平台、需要硬核调试能力、或被“加密结果不一致”问题卡住超过 3 小时的工程师。2. AES128 加密流程四层结构从 State 初始化到最终密文输出的逐轮映射AES128 的核心是一个迭代结构输入 16 字节明文 → 填入 4×4 状态矩阵 → 经过 10 轮变换 → 输出 16 字节密文。这 10 轮并非简单重复而是由四个原子操作按固定顺序组合而成且最后一轮结构微调。理解这四层的物理含义与执行边界是避免“轮数错一位、密文全乱”的前提。2.1 State 矩阵初始化明文如何填入 4×4 字节格子AES 规范明确定义明文按列优先column-major order填入 State 矩阵。这意味着输入字节数组plaintext[16] {0x00,0x11,0x22,...,0xFF}并非从左到右填满第一行而是先填第一列索引 0,4,8,12再填第二列索引 1,5,9,13依此类推。这是最容易被忽略的底层约定直接决定后续所有ShiftRows和MixColumns的操作对象。// C 语言中将明文字节数组映射到 State[4][4] 的标准方式 uint8_t plaintext[16] {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77, 0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF}; uint8_t State[4][4]; for (int c 0; c 4; c) { // 遍历列 for (int r 0; r 4; r) { // 遍历行 State[r][c] plaintext[r 4*c]; // 注意r 4*c非 r*4 c } }逻辑说明plaintext[r 4*c]中r是行索引0~3c是列索引0~3。当c0第一列时取plaintext[0], plaintext[1], plaintext[2], plaintext[3]当c1第二列时取plaintext[4], plaintext[5], plaintext[6], plaintext[7]。最终State矩阵内容为[0x00, 0x44, 0x88, 0xCC] // 第0行第0列0x00, 第1列0x44, 第2列0x88, 第3列0xCC [0x11, 0x55, 0x99, 0xDD] // 第1行 [0x22, 0x66, 0xAA, 0xEE] // 第2行 [0x33, 0x77, 0xBB, 0xFF] // 第3行参数说明此映射是 AES 标准强制要求任何自定义实现若改为行优先plaintext[r*4 c]将导致ShiftRows移位方向错误、MixColumns计算列数据错位最终密文必然与标准向量不符。2.2 初始轮密钥加Initial AddRoundKey第一道也是最易被跳过的屏障在进入主循环前必须执行一次AddRoundKey使用密钥扩展表w[]的前 4 行即w[0]到w[3]与State进行字节级异或。这一步常被误认为“可有可无”实则它是整个加密流程的起点且其异或逻辑直接影响后续所有轮变换的输入。AddRoundKey的规则是State[r][c] ^ w[c][r]注意下标顺序是w[c][r]非w[r][c]。// 密钥扩展表 w[44][4]AES128: Nr10 → w 行数4*(101)44 // 此处仅展示前4行即第0轮密钥由密钥 0x00010203...0e0f 扩展而来 uint8_t w[44][4] { {0x00, 0x01, 0x02, 0x03}, // w[0] {0x04, 0x05, 0x06, 0x07}, // w[1] {0x08, 0x09, 0x0a, 0x0b}, // w[2] {0x0c, 0x0d, 0x0e, 0x0f}, // w[3] // ... 后续40行由密钥扩展算法生成 }; // 初始 AddRoundKeyState[r][c] ^ w[c][r] for (int c 0; c 4; c) { for (int r 0; r 4; r) { State[r][c] ^ w[c][r]; } }逻辑说明w[c][r]的索引交换是关键。例如State[0][0]即0x00与w[0][0]即0x00异或得0x00State[1][0]即0x11与w[0][1]即0x01异或得0x10State[0][1]即0x44与w[1][0]即0x04异或得0x40。这确保了密钥的每个字节都精准作用于 State 的对应位置。参数说明w[c][r]的行列索引互换源于 AES 规范中对“轮密钥”存储格式的定义——w[i]表示第i个 32 位字而每个字又按字节拆分为w[i][0]最高字节到w[i][3]最低字节。AddRoundKey操作时w[i]的四个字节被分配给State的同一列ci%4故需用c索引w的行用r索引w的列。2.3 主循环10轮变换的精确边界与第10轮的特殊性AES128 的主循环执行Nr-1 9次完整轮Round每次包含SubBytes → ShiftRows → MixColumns → AddRoundKey四步第 10 轮Final Round则省略MixColumns仅执行SubBytes → ShiftRows → AddRoundKey。这个“少一步”的设计是 AES 解密能复用加密结构的关键但也是调试中最常出错的点——若在第10轮仍调用MixColumns密文将完全错误。// 主循环执行9次完整轮i 1 to 9 for (int round 1; round 9; round) { SubBytes(State); ShiftRows(State); MixColumns(State); AddRoundKey(State, w, round); // 使用 w[4*round] 到 w[4*round3] } // 第10轮省略 MixColumns SubBytes(State); ShiftRows(State); AddRoundKey(State, w, 10); // 使用 w[40] 到 w[43]逻辑说明AddRoundKey的轮密钥索引计算为w[4*round]到w[4*round3]。例如第1轮用w[4]~w[7]第2轮用w[8]~w[11]第10轮用w[40]~w[43]。SubBytes和ShiftRows是可逆操作MixColumns是线性变换三者组合构成一轮的混淆与扩散。省略MixColumns的第10轮保证了最终输出无需再进行逆列混合使解密流程能通过逆序调用InvShiftRows → InvSubBytes → AddRoundKey最后再InvMixColumns完成。参数说明round参数从 1 开始计数而非 0。w表总行数为4*(Nr1)44其中w[0]~w[3]用于初始轮w[4]~w[43]用于第1至第10轮共 40 行完美匹配10轮 × 4行/轮。2.4 SubBytesS盒查表的本质是 GF(2⁸) 上的仿射变换不是简单哈希SubBytes是唯一的非线性层它对State中每个字节独立查 S 盒Substitution Box。S 盒并非随机数表而是由两步构成1) 在有限域 GF(2⁸) 上求乘法逆元0x00 的逆元定义为 0x002) 对结果进行固定的仿射变换Affine Transformation。理解这一点才能解释为何0x00查表得0x630x01得0x7c。// S盒定义部分完整256项见标准FIPS-197 const uint8_t Sbox[256] { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, // 0x00-0x07 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, // 0x08-0x0f // ... 共256项 }; void SubBytes(uint8_t State[4][4]) { for (int r 0; r 4; r) { for (int c 0; c 4; c) { uint8_t b State[r][c]; State[r][c] Sbox[b]; // 直接查表b 作为索引 } } }逻辑说明Sbox[b]中b是原始字节值直接作为数组下标。例如State[0][0]初始为0x00查Sbox[0x00] 0x63State[1][0]初始为0x11查Sbox[0x11] 0xc9。S 盒的构造确保了高非线性度和抗差分/线性密码分析能力。参数说明S 盒是 AES 标准固化的一部分不可更改。任何自定义 S 盒都将导致与其他实现不兼容。查表操作是SubBytes的唯一正确实现方式试图用公式实时计算逆元在资源受限平台上效率极低。2.5 ShiftRows行移位不是循环右移而是按行索引偏移量左移ShiftRows是一个置换层它对State的每一行进行循环左移但移位量不同第0行不移0位第1行左移1字节第2行左移2字节第3行左移3字节。这是一个常被误实现为“统一右移”的操作其本质是打破列间相关性。void ShiftRows(uint8_t State[4][4]) { uint8_t temp[4]; // 第1行左移1: [a,b,c,d] - [b,c,d,a] temp[0] State[1][1]; temp[1] State[1][2]; temp[2] State[1][3]; temp[3] State[1][0]; for (int i 0; i 4; i) State[1][i] temp[i]; // 第2行左移2: [a,b,c,d] - [c,d,a,b] temp[0] State[2][2]; temp[1] State[2][3]; temp[2] State[2][0]; temp[3] State[2][1]; for (int i 0; i 4; i) State[2][i] temp[i]; // 第3行左移3: [a,b,c,d] - [d,a,b,c] temp[0] State[3][3]; temp[1] State[3][0]; temp[2] State[3][1]; temp[3] State[3][2]; for (int i 0; i 4; i) State[3][i] temp[i]; // 第0行保持不变 }逻辑说明移位方向是“左移”即数据向列索引减小的方向移动。例如第1行原为[0x11,0x55,0x99,0xdd]左移1后变为[0x55,0x99,0xdd,0x11]。这种不对称移位迫使原本在同一列的字节分散到不同列为MixColumns提供输入多样性。参数说明移位量由行索引r决定shift_amount r。因此第r行的字节State[r][c]将移动到新列new_c (c - r 4) % 4左移等价于列索引减r模4处理循环。2.6 MixColumns列混合是 GF(2⁸) 上的矩阵乘法不是普通整数运算MixColumns是最复杂的线性层它将State的每一列视为 GF(2⁸) 上的 4 维向量并与一个固定矩阵相乘。其核心是有限域上的加法异或和乘法非普通乘法这是导致“为什么0x02 * 0x80 0x1b”这类玄学问题的根源。// MixColumns 固定矩阵列向量左乘 // [0x02 0x03 0x01 0x01] // [0x01 0x02 0x03 0x01] // [0x01 0x01 0x02 0x03] // [0x03 0x01 0x01 0x02] // GF(2^8) 乘法辅助函数xtime(a) 0x02 * a uint8_t xtime(uint8_t a) { uint8_t ret a 1; if (a 0x80) ret ^ 0x1b; // 模不可约多项式 x^8 x^4 x^3 x 1 return ret; } void MixColumns(uint8_t State[4][4]) { for (int c 0; c 4; c) { // 对每一列 uint8_t a0 State[0][c], a1 State[1][c], a2 State[2][c], a3 State[3][c]; uint8_t b0 xtime(a0) ^ xtime(a1) ^ a1 ^ a2 ^ a3; // 0x02*a0 ^ 0x03*a1 ^ 0x01*a2 ^ 0x01*a3 uint8_t b1 a0 ^ xtime(a1) ^ xtime(a2) ^ a2 ^ a3; // 0x01*a0 ^ 0x02*a1 ^ 0x03*a2 ^ 0x01*a3 uint8_t b2 a0 ^ a1 ^ xtime(a2) ^ xtime(a3) ^ a3; // 0x01*a0 ^ 0x01*a1 ^ 0x02*a2 ^ 0x03*a3 uint8_t b3 xtime(a0) ^ a0 ^ a1 ^ xtime(a3) ^ a3; // 0x03*a0 ^ 0x01*a1 ^ 0x01*a2 ^ 0x02*a3 State[0][c] b0; State[1][c] b1; State[2][c] b2; State[3][c] b3; } }逻辑说明b0 0x02*a0 ^ 0x03*a1 ^ 0x01*a2 ^ 0x01*a3中0x03*a1被分解为(0x02 ^ 0x01)*a1 xtime(a1) ^ a1故b0 xtime(a0) ^ (xtime(a1) ^ a1) ^ a2 ^ a3。xtime()函数实现了0x02乘法左移1位若原字节最高位为1则异或0x1b即模x^4x^3x1的余数。这是MixColumns正确性的数学基石。参数说明xtime()的0x1b是 AES 标准指定的不可约多项式x^8 x^4 x^3 x 1的低8位表示。任何其他值如0x1d都将破坏扩散特性。MixColumns必须对每一列独立计算不能跨列操作。3. 密钥扩展KeyExpansion从16字节种子密钥生成44行轮密钥的确定性算法AES 的安全性不仅依赖于轮变换更依赖于轮密钥的强扩散性。密钥扩展算法KeyExpansion将外部输入的 128 位16 字节种子密钥K扩展为4*(Nr1) 44行、每行 4 字节的轮密钥表w[]。这个过程不是随机填充而是由RotWord、SubWord和轮常数Rcon三者严格驱动的确定性序列。若此处出错后续所有AddRoundKey都将作用于错误密钥导致“加密成功但无法解密”的经典翻车。3.1 密钥扩展基础Nk、Nr 与 w[] 表维度的硬性对应关系AES128 的参数是刚性的Nk 4种子密钥长度为 4 个 32 位字Nr 10加密轮数为 10因此w[]表总行数为4*(Nr1) 44。前Nk 4行w[0]~w[3]直接复制种子密钥剩余40行w[4]~w[43]由算法生成。理解Nk和Nr的数值是校验密钥扩展是否正确的第一道关卡。// AES128 固定参数 #define Nk 4 #define Nr 10 #define Nb 4 // 分组长度固定为4字128位 uint8_t w[44][4]; // 44行 × 4列 176字节轮密钥表 uint8_t key[16] {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}; // 种子密钥 void KeyExpansion() { // 步骤1将种子密钥复制到 w[0]~w[3] for (int i 0; i Nk; i) { w[i][0] key[4*i]; w[i][1] key[4*i1]; w[i][2] key[4*i2]; w[i][3] key[4*i3]; } // 步骤2生成 w[4] 到 w[43] for (int i Nk; i 4*(Nr1); i) { uint8_t temp[4] {w[i-1][0], w[i-1][1], w[i-1][2], w[i-1][3]}; if (i % Nk 0) { // i 是 Nk 的倍数执行 RotWord → SubWord → 异或 Rcon[i/Nk] RotWord(temp); SubWord(temp); temp[0] ^ Rcon[i/Nk][0]; // Rcon 表只影响第0字节 } else if (Nk 8 i % Nk 4) { // AES256 特有分支AES128 不执行 SubWord(temp); } // 最终w[i] w[i-Nk] XOR temp w[i][0] w[i-Nk][0] ^ temp[0]; w[i][1] w[i-Nk][1] ^ temp[1]; w[i][2] w[i-Nk][2] ^ temp[2]; w[i][3] w[i-Nk][3] ^ temp[3]; } }逻辑说明w[i]的生成依赖于w[i-1]和w[i-Nk]。当i % Nk 0即i4,8,12,...,40触发增强模式对w[i-1]先RotWord循环左移再SubWordS盒查表最后与Rcon[i/Nk]异或。Rcon表的索引i/Nk对应轮数如i4时i/Nk1取Rcon[1]。参数说明Rcon是一个预定义的常数表其第i行为[0x01i-1, 0x00, 0x00, 0x00]其中是左移且0x01i-1在 GF(2⁸) 中计算即xtime迭代。Rcon[1] {0x01,0x00,0x00,0x00},Rcon[2] {0x02,0x00,0x00,0x00},Rcon[3] {0x04,0x00,0x00,0x00}依此类推。3.2 RotWord 与 SubWord密钥扩展的两个核心原子操作RotWord和SubWord是密钥扩展的“引擎”。RotWord对一个 4 字节数组进行循环左移[a,b,c,d] → [b,c,d,a]SubWord则对每个字节独立查 S 盒。二者组合将前一轮密钥的“熵”注入到新轮密钥中确保轮密钥间具有高度非线性相关性。void RotWord(uint8_t word[4]) { uint8_t temp word[0]; word[0] word[1]; word[1] word[2]; word[2] word[3]; word[3] temp; } void SubWord(uint8_t word[4]) { for (int i 0; i 4; i) { word[i] Sbox[word[i]]; // 复用 SubBytes 中的 Sbox 表 } }逻辑说明RotWord是纯粹的字节重排无数学运算SubWord复用SubBytes的 S 盒保证了密钥扩展与数据变换使用同一非线性源。例如w[3] {0x0c,0x0d,0x0e,0x0f}经RotWord后为{0x0d,0x0e,0x0f,0x0c}再经SubWord查表Sbox[0x0d]0x5c,Sbox[0x0e]0x3f,Sbox[0x0f]0x04,Sbox[0x0c]0x25得{0x5c,0x3f,0x04,0x25}。参数说明SubWord必须使用与SubBytes完全相同的 S 盒。任何差异如大小写、索引偏移都将导致轮密钥错误。RotWord的循环方向是左移右移将破坏密钥扩展的规范一致性。3.3 Rcon 轮常数表驱动密钥扩展非线性的“时间戳”Rcon[i]是一个 4 字节数组其形式为[rc_i, 0x00, 0x00, 0x00]其中rc_i是 GF(2⁸) 中0x02的i-1次幂。它像一个“轮次计数器”确保即使种子密钥有规律如全零生成的轮密钥也具备充分的雪崩效应。// Rcon 表前11项对应 AES128 的 10 轮 初始轮 const uint8_t Rcon[11][4] { {0x00, 0x00, 0x00, 0x00}, // Rcon[0] 未使用 {0x01, 0x00, 0x00, 0x00}, // Rcon[1] 0x02^0 {0x02, 0x00, 0x00, 0x00}, // Rcon[2] 0x02^1 {0x04, 0x00, 0x00, 0x00}, // Rcon[3] 0x02^2 {0x08, 0x00, 0x00, 0x00}, // Rcon[4] 0x02^3 {0x10, 0x00, 0x00, 0x00}, // Rcon[5] 0x02^4 {0x20, 0x00, 0x00, 0x00}, // Rcon[6] 0x02^5 {0x40, 0x00, 0x00, 0x00}, // Rcon[7] 0x02^6 {0x80, 0x00, 0x00, 0x00}, // Rcon[8] 0x02^7 {0x1b, 0x00, 0x00, 0x00}, // Rcon[9] 0x02^8 mod (x^8x^4x^3x1) {0x36, 0x00, 0x00, 0x00}, // Rcon[10] 0x02^9 };逻辑说明Rcon[i]仅第0字节有效其余为0。Rcon[1]用于生成w[4]Rcon[2]用于w[8]依此类推。Rcon[9] 0x1b是0x02^8 0x100模0x11b即x^8x^4x^3x1的余数Rcon[10] 0x36是0x02^9 0x200模0x11b的余数。这是密钥扩展中 GF(2⁸) 运算的直接体现。参数说明Rcon表是 AES 标准固化部分不可修改。Rcon[0]未被使用索引从1开始。Rcon[i]的值必须精确匹配 FIPS-197 标准否则轮密钥将偏离预期。3.4 密钥扩展全流程手算验证以 w[4] 生成为例为彻底掌握密钥扩展我们手动计算w[4]的生成过程。种子密钥key {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}。初始化w[0]~w[3]w[0] {0x00,0x01,0x02,0x03}w[1] {0x04,0x05,0x06,0x07}w[2] {0x08,0x09,0x0a,0x0b}w[3] {0x0c,0x0d,0x0e,0x0f}计算w[4]i4,i%Nk0触发增强模式temp w[3] {0x0c,0x0d,0x0e,0x0f}RotWord(temp) → {0x0d,0x0e,0x0f,0x0c}SubWord(temp)查 S 盒Sbox[0x0d]0x5c,Sbox[0x0e]0x3f,Sbox[0x0f]0x04,Sbox[0x0c]0x25→{0x5c,0x3f,0x04,0x25}temp[0] ^ Rcon[4/4][0] Rcon[1][0] 0x01→{0x5d,0x3f,0x04,0x25}w[4] w[本文还有配套的精品资源点击获取