和DES加密算法相比,AES提供了更高的安全性,在TLS握手后的数据传输阶段,所使用的共享密钥算法就是AES加密模式。
AES, Advanced Encryption Standard,其实是一种FIPS 197标准。美国NIST (National INstitute of Standards and Technology)在1997年公开征集的替代DES加密算法中,有5种算法入选决赛,即MARS,RC6,Rijndael,Serpent和Twofish。经过多轮的测试验证讨论,Rijndael算法最终入选。我们目前所使用的AES,其实就是利时学者Joan Daemen和Vincent Rijmen所提出的Rijndael算法。
AES和Rijndael算法总览
Rijndael算法支持多种分组及密钥长度,只要该长度为128-256之间32的倍数。而AES标准支持的分组大小固定为128位,密钥长度有3种选择:128位、192位及256位。密钥长度的不同,最终体现在加密轮数的差异上。
和DES所使用的Feistel网络结构不同,Rijndael采用的是SPN(Substitution-permutation network)结构进行迭代计算。每一次的迭代称为一轮(又称为轮函数),轮函数分别由SubBytes、ShiftRows、MixColumns和AddRoundKey共4个步骤组成。其中的步骤AddRoundKey涉及到密钥扩展(KeyExpansion)。总的来说,Rijndael算法包括两大部分,明文的迭代计算和密钥扩展。
本文以128bit(即16 Byte)明文和密钥长度为例,来分析讨论Rijndael加密算法的构成以及部分C语言实现,而解密算法为加密算法的逆运算,为了集中重点,解密算法本文不作分析。而当明文超过128bit时,需要使用分组加密模式进行迭代,加密模式也不在本文的讨论范围。
Rijndael 128bit(16 Byte)加密模式总览图如下:
一、明文分组的迭代算法
当明文分组长度为128bit,16Byte时,将其分为4x4 Byte的矩阵。如果密钥长度为128位,则分为11轮迭代计算,除去第一轮和最后一轮外(见上图),其他9轮为以下4个步骤的迭代:
SubBytes-->ShiftRows-->MixColumns-->AddRoundKey
1. SubBytes
即字节置换(Substitute Bytes)。
128bit的明文分组重组的4x4字节矩阵中,每一个字节通过S盒进行置换,和Des不同,SubBytes代码实现中,常量表示的S盒,有着严格的数学定义。
单个字节a的置换过程如下:
- 取该字节a的高4位,用该值定位于S盒的行号
- 取该字节a的低4位,用该值定位于S盒的列号
- 用上述已定位的行和列的交叉点字节替换原数据字节a
示例如下:
C语言实现代码如下:
void aes_sub_bytes(uint8_t *state)
{
int i, j;
for (i = 0; i < 4; i++) { /* 4 Rows */
for (j = 0; j < 4; j++) {
state[i * 4 + j] = aes_sbox[state[i * 4 + j]]; /* column */
}
}
}
上述代码中,aes_sbox为256字节的常量表,其内容为上图左侧表格中的数据值。
2. ShiftRows(行移位)
行移位主要目的是实现字节在每一行的扩散,属于线性变换。
4x4的字节矩阵通过以下规则进行循环字节移位:
状态矩阵的第0行左移0字节,第1行循环左移1字节,第2行循环左移2字节,第3行循环左移3字节。
如图:
3. Mix Columns(列混合)
轮函数的4个步骤中,列混合最为复杂。
列混合是通过将第2步行移位后的矩阵与常矩阵C相乘以实现在列上的扩散,属于代替变换,其实质是在有限域GF(2^8)上的多项式乘法运算。
混合运算过程如下:
即a'1,0为中间表中第一列与常量表S中的第一行相乘,a'1,0为中间表中第一列与常量表S中的第二行相乘,a'2,0为中间表中第一列与常量表S中的第三行相乘,a'3,0为中间表中第一列与常量表S中的第四行相乘。
这里的行列相乘,即上图中同色块元素的相乘。
首先介绍一些矩阵元素运算的背景知识。
矩阵元素的乘法和加法都是定义在基于GF(2^8)上的二元运算,并不是通常意义上的乘法和加法。这里涉及到一些信息安全上的数学知识,这种二元运算的加法等价于两个字节的异或,乘法则复杂一点。
- 单个字节a的二元运算x2的算法规则为:
如果该字节a的bit7(最高位)为1,则和0x1B异或;否则,a左移一位,低位补0。- 单个字节a的二元运算x3的算法规则为:
为x2的合成运算,即3 x a = (2 x a) ⊕ a
下面以a'0,0为例,具体解析其计算获得过程:
a'0,0= (2 x a0,0) ⊕ (3 x a1,0) ⊕ a2,0⊕ a3,0
然后将2x,3 x带入上面背景知识中的二次元相应运算即可。
列混合的C代码实现如下:
uint8_t Y[16] = {
2, 3, 1, 1,
1, 2, 3, 1,
1, 1, 2, 3,
3, 1, 1, 2
};
void aes_mix_columns(uint8_t *state)
{
uint8_t s[4];
int j, r;
for (r = 0; r < 4; r++) { /* 为Y表行索引,共4行 */
s[r] = 0;
for (j = 0; j < 4; j++) { /* j为一列中的一个元数据 */
/* */
s[r] = s[r] ^ aes_mul(state[j], Y[r * 4 + j]);
}
}
for (r = 0; r < 4; r++) {
state[r] = s[r];
}
}
上面代码中的函数aes_mul实现如下:
uint8_t mul2(uint8_t state)
{
return ((state << 1) ^ (((state >> 7) & 1) * 0x1b));
}
uint8_t aes_mul(uint8_t state, uint8_t x)
{
if (x == 1)
return state;
else if (x == 2)
return mul2(state);
else if (x == 3)
return mul2(state) ^ state;
}
上述代码只考虑了x1,x2,x3的情况,且未考虑其他无效的返回值。
4. AddRoundKey(轮密钥加)
轮密钥加比较简单,将轮密钥与其前一步(注意第一轮和最后一轮迭代步骤)的矩阵状态值进行逐字节异或。
二、密钥扩展
密钥扩展算法目的是根据种子密钥生成多组轮密钥。对应128、196和256位密钥,分别扩展生成11、13、15组轮钥,这里的11、13,15即为11 x 4 word、13 x 4 word,15 x 4 word,每word 32bit,即128bit。其实质是根据迭代轮数不同,生成相应轮数的轮密钥。
下面以128bit密钥为例,来说明轮密钥的生成过程。
1. 密钥重组
128bit的原始密钥组成4x4的字节矩阵,并将一列4个字节重组为一个字(word),共4W:
2. 密钥扩展
密钥扩展流程如下(j为迭代轮数,1<= j <= 10):
其中第一轮密钥为原始密钥,后续的10轮密钥均由轮密钥扩展计算得到。
其扩展规则为:
1).如果i不是4的倍数,那么第i列由如下等式确定:
W[4j + i]=W[4j + i-4] ⨁ W[4j + i-1]
其中1<= i <= 3。
2).如果i为0,即4j+i为4的倍数,那么第j轮第0列由如下等式确定:
W[4j]=W[4j-4] ⨁ T(W[4j -1])
T函数定义为:
即T函数由“字循环移位+S盒置换+常量表异或”三个步骤构成。
实现代码略。
参考:
AES加密算法的详细介绍与实现_TimeShatter的博客-CSDN博客_aes加密
https://github.com/matt-wu/AES
《图解密码学》
《https权威指南》