推一下大佬写的文章,本文中部分内容转载自此处:AES加密算法的详细介绍与实现
算法简介
高级加密标准(AES,Advanced Encryption Strandard)为最常见对称加密算法
加密流程如下:
AES为分组密码,加密过程中将明文分成多个长度相同的组,每次加密一组数据,直到加密完整个明文。
在AES标准规范中,分组长度只能是128位,每个分组为16个字节(每个字节8位)。
密钥的长度可以使用128位、192位或256位。
密钥的长度不同,推荐的加密轮数也不同,如下表所示:
AES |
密钥长度(32位比特字) |
分组长度(32位比特字) |
加密轮数 |
AES-128 |
4 |
4 |
10 |
AES-192 |
6 |
4 |
12 |
AES-256 |
8 |
4 |
14 |
以AES-128为例,128位的明文分组P和密钥K都被分成了16个字节,分别记为 P = P0 P1 …… P15 和 K = K0 K1 …… K15
一般地,明文用字节为单位的正方形矩阵描述,称为状态矩阵。
在算法的每一轮中,状态矩阵的内容不断变化最后作为密文输出。
矩阵中字节的排列顺序为从上到下、从左至右依次排列:
AES的整体结构如下图所示,其中的W[0,3]是指W[0]、W[1]、W[2]和W[3]串联组成的128位密钥。
加密的第1轮到第9轮的轮函数一样,包括4个操作:字节代换、行位移、列混合和轮密钥加。最后一轮迭代不执行列混合。
另外,在第一轮迭代之前,先将明文和原始密钥进行一次异或加密操作。
算法操作
字节代换
AES定义了一个S盒和一个逆S盒。
其中字节代换操作用到S盒
字节代换逆操作用到逆S盒
字节代换操作
状态矩阵中的元素按照以下方式映成为一个一个新的字节:将该字节的高四位作为行值,低4位作为列值,取出S盒或者逆S盒中对应的行的元素作为输出。
例如:加密时输出的字节S1为0x12,则查S盒的第0x01行盒第0x02列,得到值0xc9,然后替换S1原有的0x12为0xc9,如下图所示:
字节代换代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static int getNumFromSBox(int index) { int row = getLeft4Bit(index); int col = getRight4Bit(index); return S[row][col]; }
static void subBytes(int array[4][4]) { for(int i = 0;i < 4;i++) for(int j = 0;j < 4;j++) array[i][j] = getNumFromSBox(aeeay[i][j]); }
|
字节代换逆操作
与字节代换操作一致,只是由S盒变为了逆S盒
行移位
行移位操作
左循环移位操作。当密钥长度为128比特时,状态矩阵的第0行左移0字节,第1行左移1字节,第2行左移2字节,第三行左移3字节,如下图所示:
行移位的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
static void leftLoop4int(int array[4],int step) { int temp[4]; for(int i = 0;i < 4;i++) temp[i] = array[i];
int index = step % 4 == 0 ? 0 : step % 4; for(int i = 0;i < 4;i++) { array[i] = temp[index]; index ++; index = index % 4; } }
static void shiftRows(int array[4][4]) { int rowTwo[4],rowThree[4],rowFour[4]; for(int i = 0,i < 4;i++) { rowTwo[i] = array[1][i]; rowThree[i] = array[2][]i]; rowFour[i] = array[3][i]; } leftLoop4int(rowTwo ,1); leftLoop4int(rowThree,2); leftLoop4int(rowFour, 3); for(int i = 0;i < 4;i++) { array[1][i] = rowTwo[i]; array[2][i] = rowThree[i]; array[3][i] = rowFour[i]; } }
|
行移位的逆变换
将状态矩阵中的每一行执行相反的移位操作,即左移变为右移
列混合
列混合操作
经行移位后的状态矩阵与固定的矩阵相乘,得到混淆后的状态矩阵
如图:
列混合的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
|
static const int colM[4][4] = { 2,3,1,1, 1,2,3,1, 1,1,2,3, 3,1,1,2 };
static int GFMul2(int s) { int result = s << 1; int a7 = result $ 0x00000100;
if(a7 != 0) { result = result & 0x000000ff; result = result ^ 0x1b; }
return result; }
static int GFMul3(int s) { return GFMul2(s) ^ s; }
static int GFMul(int n, int s) { int result;
if(n == 1) result = s; else if(n ==2) result = GFMul2(s); else if(n == 3) result = GFMul3(s); else if(n == 0x9) result = GFMul9(s); else if(n == 0xb) result = GFMul11(s); else if(n == 0xd) result = GFMul13(s); else if(n == 0xe) result = GFMul14(s);
return result; }
static void mixColumns(int array[4][4]) { int tempArray[4][4];
for(int i = 0; i < 4; i++) for(int j = 0; j < 4; j++) tempArray[i][j] = array[i][j];
for(int i = 0; i < 4; i++) for(int j = 0; j < 4; j++){ array[i][j] = GFMul(colM[i][0],tempArray[0][j]) ^ GFMul(colM[i][1],tempArray[1][j]) ^ GFMul(colM[i][2],tempArray[2][j]) ^ GFMul(colM[i][3], tempArray[3][j]); } }
|
列混合逆运算
可由下图的矩阵乘法定义:
可以验证:逆变换矩阵同正变换矩阵的乘积恰好为单位矩阵
轮密钥加
将128为轮密钥Ki同状态矩阵中的数据进行逐位异或操作。
其中,密钥Ki中每个字W[4i],W[4i+1],W[4i+2],W[4i+3]为32位比特字,包含4个字节。
可以看成S0 S1 S2 S3 组成的32位字与W[4i]的异或运算。
轮密钥加的逆运算同正向的轮密钥加运算完全一致,因为异或的逆操作是其自身。
轮密钥加的实现
1 2 3 4 5 6 7 8 9 10 11 12
| static void addRoundKey(int array[4][4], int round) { int warray[4]; for(int i = 0; i < 4; i++) {
splitIntToArray(w[ round * 4 + i], warray);
for(int j = 0; j < 4; j++) { array[j][i] = array[j][i] ^ warray[j]; } } }
|
密钥扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| static int w[44];
static void extendKey(char *key) { for(int i = 0; i < 4; i++) w[i] = getWordFromStr(key + i * 4);
for(int i = 4, j = 0; i < 44; i++) { if( i % 4 == 0) { w[i] = w[i - 4] ^ T(w[i - 1], j); j++; }else { w[i] = w[i - 4] ^ w[i - 1]; } } }
|
T()函数的代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
static const int Rcon[10] = { 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 };
static int T(int num, int round) { int numArray[4]; splitIntToArray(num, numArray); leftLoop4int(numArray, 1);
for(int i = 0; i < 4; i++) numArray[i] = getNumFromSBox(numArray[i]);
int result = mergeArrayToInt(numArray); return result ^ Rcon[round]; }
|
AES解密函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
void deAes(char *c, int clen, char *key) {
int keylen = strlen(key); if(clen == 0 || clen % 16 != 0) { printf("密文字符长度必须为16的倍数!现在的长度为%d\n",clen); exit(0); }
if(!checkKeyLen(keylen)) { printf("密钥字符长度错误!长度必须为16、24和32。当前长度为%d\n",keylen); exit(0); }
extendKey(key); int cArray[4][4]; for(int k = 0; k < clen; k += 16) { convertToIntArray(c + k, cArray);
addRoundKey(cArray, 10);
int wArray[4][4]; for(int i = 9; i >= 1; i--) { deSubBytes(cArray);
deShiftRows(cArray);
deMixColumns(cArray); getArrayFrom4W(i, wArray); deMixColumns(wArray);
addRoundTowArray(cArray, wArray); }
deSubBytes(cArray);
deShiftRows(cArray);
addRoundKey(cArray, 0);
convertArrayToStr(cArray, c + k);
} }
|