AES算法及具体步骤
- AES 高级加密标准
- 前言
- AES背景
- 算法描述
- 算法流程图
- State Matrix
- Key Expansion
- g函数
- AddRoundKey
- Round Function
- 1. SubBytes
- 2. ShiftRows
- 3. MixColumns
- 4. Add round key
- Final Round
AES 高级加密标准
前言
之前一直想写CSDN博客但是没有找到可以写的内容,趁着最近刚好在学密码学就可以把自己学习时候的笔记贴上来啦。因为比较菜…所以内容几乎都是参考了教材、百度和wiki里面的,自己整理重述了一下~
AES背景
高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
该算法为比利时密码学家Joan Daemen和Vincent Rijmen所设计,结合两位作者的名字,以Rijndael为名投稿高级加密标准的甄选流程。(Rijndael的发音近于"Rhine doll")
算法描述
在原始的Rijndael算法中分组长度和密钥长度都可变,各自可以独立的指定为128 bit、192 bit、256 bit。在AES中,分组长度只能是128 bit,也就是16个字节。(1个字节等于8位,1 Byte = 8 Bit)。密钥长度可以为三者中的任意一种。最广泛 的使用是密钥128 bit,迭代轮数10轮。
AES算法 | 密钥长度(32 bit) | 分组长度(32 bit) | 加密和解密轮数 |
AES-128 | 4 | 4 | 10 |
AES-192 | 6 | 4 | 12 |
AES-256 | 8 | 4 | 14 |
对称密钥使用相同的密钥进行加密和解密,因此发送者和接受者都必须知道并使用相同的秘密密钥。 本文主要基于AES-128。
算法流程图
这个流程图是在github上看到的,自己画的有点简陋,这个图非常清晰就拿来了2333
(后面有几张图好像也是,我把原链接贴在这里)
图源 link
自己重新整理了一下画的流程图大概长这样:
Round Function
9 times
Final Round
PlainText
AddRoundKey
Key
KeyExpansion
SubBytes
ShiftRows
MixColumns
AddRoundKey
SubBytes
ShiftRows
AddRoundKey
CipherText
下面就详细的讲一些流程图里面的每一个部分
State Matrix
Block cipher is a cryptosystem which encrypts data not by bit but by block,which is group of bits,applying algorithm per block.
分组密码不是按位对数据进行加密,而是按每一组对数据进行整体加密算法。所以首先对明文、密钥进行分组,每一组为1 byte(8 bit),16 x 8=128 bit,因此一共分成了16组,用 4 x 4矩阵表示,该矩阵就称为状态矩阵(State Matrix)。对应上图AddRoundKey和Key Expansion上方的矩阵。
用C++实现如下:
//将明文分组拷贝到状态矩阵中
unsigned char state[16];
for (int i = 0; i < 16; i++)
{
state[i] = message[i];
}
Key Expansion
Key Expansion是将用户输入的key扩展生成多组轮密钥。对应AES-128,AES-192,AES-256,分别扩展出11,13,15组。
假设我们所用的加密方法为AES-128标准,则需要将4个字(1 word = 32 bit)的原始密钥扩展成44个字的轮密钥(4 x 11轮)。具体步骤如下图所示:
图源link
g函数
g函数是一个复杂函数,由三个步骤构成:字循环,字节代换和轮常量异或。
- 字循环将1个字的4个字节循环左移1个字节,即将字[b0,b1,b2,b3]变为[b1,b2,b3,b0]。
- 字节代换基于S盒对输入字中的每个字节进行S代替。
- 轮常量异或轮常量Rcon[i]数据表如下:
eg: 假如第8回合的回合密钥为:
EA D2 73 21 B5 8D BA D2 31 2B F5 60 7F 8D 29 2F
那么,第9回合密钥的前4个字节(第一列)的计算过程如下:
temp | 字循环 | 字节代换 | Rcon[9] | XOR Rcon | w[i-4] | w[i] |
7F8D292F | 8D292F7F | 5DA515D2 | 1B000000 | 46A515D2 | EZD27321 | AC7766F3 |
- temp存第8回合密钥的最后一个字(最后一列)
- 字循环和字节代换都是对temp数组里的数据
- Rcon[9]由上图数据表得出
- XOR Rcon是将temp与Rcon[9]异或此时完成了上图中w到w’的步骤
- w[i-4]是第8回合密钥的第1个字(第一列)
- w[i]为最后结果,w[i]=w’ XOR w[i-4]
AddRoundKey
Add round key其实就是将128位的密钥和128位的明文进行逐位异或操作。
用C++实现如下
void AddRoundKey(state, roundkey)
{
//roundkey 和 state XOR!
for (int i = 0; i < 16; i++)
{
state[i] ^= roundkey[i];
}
}
Round Function
1. SubBytes
将字节代换的各种可能字节的变换结果排成一个表,称为AES的字节代替表或者S盒。状态矩阵中的元素按照下面的方式通过S盒映射为一个新的字节:把该字节的高4位作为行值,低4位作为列值,取出S盒对应行列的元素作为输出。例如,加密时输入字节 0x12,则查找S盒的第 0x01行,0x02 列,得到的值为 0xC9。S盒如下:
用C++实现如下:
void SubBytes(unsigned char* state)
{
//S盒字节替换
for (int i = 0; i < 16; i++)
{
state[i] = s_box[state[i]];
}
}
2. ShiftRows
行位移是将状态矩阵的各行进行循环移位,不同状态行的位移量不同。第0行不移动,第1行循环左移1个字节,第2行循环左移2个字节,第3行循环左移3个字节。如图所示:
图源自link
用C++实现如下:
void ShiftRows(unsigned char* state)
{
unsigned char tmp[16];
//tmp暂存行位移的结果
tmp[0] = state[0];
tmp[1] = state[5];
tmp[2] = state[10];
tmp[3] = state[15];
tmp[4] = state[4];
tmp[5] = state[9];
tmp[6] = state[14];
tmp[7] = state[3];
tmp[8] = state[8];
tmp[9] = state[13];
tmp[10] = state[2];
tmp[11] = state[7];
tmp[12] = state[12];
tmp[13] = state[1];
tmp[14] = state[6];
tmp[15] = state[11];
//行位移
for (int i = 0; i < 16; i++)
{
state[i] = tmp[i];
}
}
3. MixColumns
列混合是一个替代操作,是AES算法中最复杂的一步,只在第0到r-1轮中用到,最后一轮不使用该变换。是通过将state矩阵与常矩阵C相乘以达到在列上的扩散,实质是在有限域GF(256)上的多项式乘法运算。
其中常矩阵C为:
用C++实现如下:
void MixColumns(unsigned char* state)
{
unsigned char tmp[16];
//用临时数组保存
tmp[0] = (unsigned char)(mul2[state[0]] ^ mul3[state[1]] ^ state[2] ^ state[3]);
tmp[1] = (unsigned char)(state[0] ^ mul2[state[1]] ^ mul3[state[2]] ^ state[3]);
tmp[2] = (unsigned char)(state[0] ^ state[1] ^ mul2[state[2]] ^ mul3[state[3]]);
tmp[3] = (unsigned char)(mul3[state[0]] ^ state[1] ^ state[2] ^ mul2[state[3]]);
tmp[4] = (unsigned char)(mul2[state[4]] ^ mul3[state[5]] ^ state[6] ^ state[7]);
tmp[5] = (unsigned char)(state[4] ^ mul2[state[5]] ^ mul3[state[6]] ^ state[7]);
tmp[6] = (unsigned char)(state[4] ^ state[5] ^ mul2[state[6]] ^ mul3[state[7]]);
tmp[7] = (unsigned char)(mul3[state[4]] ^ state[5] ^ state[6] ^ mul2[state[7]]);
tmp[8] = (unsigned char)(mul2[state[8]] ^ mul3[state[9]] ^ state[10] ^ state[11]);
tmp[9] = (unsigned char)(state[8] ^ mul2[state[9]] ^ mul3[state[10]] ^ state[11]);
tmp[10] = (unsigned char)(state[8] ^ state[9] ^ mul2[state[10]] ^ mul3[state[11]]);
tmp[11] = (unsigned char)(mul3[state[8]] ^ state[9] ^ state[10] ^ mul2[state[11]]);
tmp[12] = (unsigned char)(mul2[state[12]] ^ mul3[state[13]] ^ state[14] ^ state[15]);
tmp[13] = (unsigned char)(state[12] ^ mul2[state[13]] ^ mul3[state[14]] ^ state[15]);
tmp[14] = (unsigned char)(state[12] ^ state[13] ^ mul2[state[14]] ^ mul3[state[15]]);
tmp[15] = (unsigned char)(mul3[state[12]] ^ state[13] ^ state[14] ^ mul2[state[15]]);
//将数据复制到state
for (int i = 0; i < 16; i++)
{
state[i] = tmp[i];
}
}
4. Add round key
与初始Add round key一样。
Final Round
由上面流程图可以看出,最后一轮没有MixColumns,其他SubBytes,ShiftRows,AddRoundKey都与上述步骤一样。
内容基本就是这么多了,可能会有错误的地方,如果有任何问题欢迎评论~蟹蟹