AES加密算法的原理详解与实现分析
AES简介
高级加密标准(AES,AdvancedEncryptionStandard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。对称加密算法也就是加密和解密用相同的密钥,具体的加密流程如下图:
密码学中的高级加密标准(AdvancedEncryptionStandard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。
这个标准用来替代原先的DES(DataEncryptionStandard),已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPSPUB197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
该算法为比利时密码学家JoanDaemen和VincentRijmen所设计,结合两位作者的名字,以Rijdael之名命之,投稿高级加密标准的甄选流程。(Rijdael的发音近于"Rhinedoll"。)
下面简单介绍下各个部分的作用与意义:
- 明文P
没有经过加密的数据。
- 密钥K
用来加密明文的密码,在对称加密算法中,加密与解密的密钥是相同的。密钥为接收方与发送方协商产生,但不可以直接在网络上传输,否则会导致密钥泄漏,通常是通过非对称加密算法加密密钥,然后再通过网络传输给对方,或者直接面对面商量密钥。密钥是绝对不可以泄漏的,否则会被攻击者还原密文,窃取机密数据。
- AES加密函数
设AES加密函数为E,则C=E(K,P),其中P为明文,K为密钥,C为密文。也就是说,把明文P和密钥K作为加密函数的参数输入,则加密函数E会输出密文C。
- 密文C
经加密函数处理后的数据
- AES解密函数
设AES解密函数为D,则P=D(K,C),其中C为密文,K为密钥,P为明文。也就是说,把密文C和密钥K作为解密函数的参数输入,则解密函数会输出明文P。
在这里简单介绍下对称加密算法与非对称加密算法的区别。
- 对称加密算法
加密和解密用到的密钥是相同的,这种加密方式加密速度非常快,适合经常发送数据的场合。缺点是密钥的传输比较麻烦。
- 非对称加密算法
加密和解密用的密钥是不同的,这种加密方式是用数学上的难解问题构造的,通常加密解密的速度比较慢,适合偶尔发送数据的场合。优点是密钥传输方便。常见的非对称加密算法为RSA、ECC和EIGamal。
实际中,一般是通过RSA加密AES的密钥,传输到接收方,接收方解密得到AES密钥,然后发送方和接收方用AES密钥来通信。
本文下面AES原理的介绍参考自《现代密码学教程》,AES的实现在介绍完原理后开始。
AES的基本结构
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位,加密轮数为10轮。
上面说到,AES的加密公式为C=E(K,P),在加密函数E中,会执行一个轮函数,并且执行10次这个轮函数,这个轮函数的前9次执行的操作是一样的,只有第10次有所不同。也就是说,一个明文分组会被加密10轮。AES的核心就是实现一轮中的所有操作。
AES的处理单位是字节,128位的输入明文分组P和输入密钥K都被分成16个字节,分别记为P=P0P1…P15和K=K0K1…K15。如,明文分组为P=abcdefghijklmnop,其中的字符a对应P0,p对应P15。一般地,明文分组用字节为单位的正方形矩阵描述,称为状态矩阵。在算法的每一轮中,状态矩阵的内容不断发生变化,最后的结果作为密文输出。该矩阵中字节的排列顺序为从上到下、从左至右依次排列,如下图所示:
现在假设明文分组P为"abcdefghijklmnop",则对应上面生成的状态矩阵图如下:
上图中,0x61为字符a的十六进制表示。可以看到,明文经过AES加密后,已经面目全非。
类似地,128位密钥也是用字节为单位的矩阵表示,矩阵的每一列被称为1个32位比特字。通过密钥编排函数该密钥矩阵被扩展成一个44个字组成的序列W[0],W[1],…,W[43],该序列的前4个元素W[0],W[1],W[2],W[3]是原始密钥,用于加密运算中的初始密钥加(下面介绍);后面40个字分为10组,每组4个字(128比特)分别用于10轮加密运算中的轮密钥加,如下图所示:
上图中,设K=“abcdefghijklmnop”,则K0=a,K15=p,W[0]=K0K1K2K3=“abcd”。
AES的整体结构如下图所示,其中的W[0,3]是指W[0]、W[1]、W[2]和W[3]串联组成的128位密钥。加密的第1轮到第9轮的轮函数一样,包括4个操作:字节代换、行位移、列混合和轮密钥加。最后一轮迭代不执行列混合。另外,在第一轮迭代之前,先将明文和原始密钥进行一次异或加密操作。
上图也展示了AES解密过程,解密过程仍为10轮,每一轮的操作是加密操作的逆操作。由于AES的4个轮操作都是可逆的,因此,解密操作的一轮就是顺序执行逆行移位、逆字节代换、轮密钥加和逆列混合。同加密操作类似,最后一轮不执行逆列混合,在第1轮解密之前,要执行1次密钥加操作。
下面分别介绍AES中一轮的4个操作阶段,这4分操作阶段使输入位得到充分的混淆。
一、字节代换
1.字节代换操作
AES的字节代换其实就是一个简单的查表操作。AES定义了一个S盒和一个逆S盒。
AES的S盒:
行/列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0x63 | 0x7c | 0x77 | 0x7b | 0xf2 | 0x6b | 0x6f | 0xc5 | 0x30 | 0x01 | 0x67 | 0x2b | 0xfe | 0xd7 | 0xab | 0x76 |
1 | 0xca | 0x82 | 0xc9 | 0x7d | 0xfa | 0x59 | 0x47 | 0xf0 | 0xad | 0xd4 | 0xa2 | 0xaf | 0x9c | 0xa4 | 0x72 | 0xc0 |
2 | 0xb7 | 0xfd | 0x93 | 0x26 | 0x36 | 0x3f | 0xf7 | 0xcc | 0x34 | 0xa5 | 0xe5 | 0xf1 | 0x71 | 0xd8 | 0x31 | 0x15 |
3 | 0x04 | 0xc7 | 0x23 | 0xc3 | 0x18 | 0x96 | 0x05 | 0x9a | 0x07 | 0x12 | 0x80 | 0xe2 | 0xeb | 0x27 | 0xb2 | 0x75 |
4 | 0x09 | 0x83 | 0x2c | 0x1a | 0x1b | 0x6e | 0x5a | 0xa0 | 0x52 | 0x3b | 0xd6 | 0xb3 | 0x29 | 0xe3 | 0x2f | 0x84 |
5 | 0x53 | 0xd1 | 0x00 | 0xed | 0x20 | 0xfc | 0xb1 | 0x5b | 0x6a | 0xcb | 0xbe | 0x39 | 0x4a | 0x4c | 0x58 | 0xcf |
6 | 0xd0 | 0xef | 0xaa | 0xfb | 0x43 | 0x4d | 0x33 | 0x85 | 0x45 | 0xf9 | 0x02 | 0x7f | 0x50 | 0x3c | 0x9f | 0xa8 |
7 | 0x51 | 0xa3 | 0x40 | 0x8f | 0x92 | 0x9d | 0x38 | 0xf5 | 0xbc | 0xb6 | 0xda | 0x21 | 0x10 | 0xff | 0xf3 | 0xd2 |
8 | 0xcd | 0x0c | 0x13 | 0xec | 0x5f | 0x97 | 0x44 | 0x17 | 0xc4 | 0xa7 | 0x7e | 0x3d | 0x64 | 0x5d | 0x19 | 0x73 |
9 | 0x60 | 0x81 | 0x4f | 0xdc | 0x22 | 0x2a | 0x90 | 0x88 | 0x46 | 0xee | 0xb8 | 0x14 | 0xde | 0x5e | 0x0b | 0xdb |
A | 0xe0 | 0x32 | 0x3a | 0x0a | 0x49 | 0x06 | 0x24 | 0x5c | 0xc2 | 0xd3 | 0xac | 0x62 | 0x91 | 0x95 | 0xe4 | 0x79 |
B | 0xe7 | 0xc8 | 0x37 | 0x6d | 0x8d | 0xd5 | 0x4e | 0xa9 | 0x6c | 0x56 | 0xf4 | 0xea | 0x65 | 0x7a | 0xae | 0x08 |
C | 0xba | 0x78 | 0x25 | 0x2e | 0x1c | 0xa6 | 0xb4 | 0xc6 | 0xe8 | 0xdd | 0x74 | 0x1f | 0x4b | 0xbd | 0x8b | 0x8a |
D | 0x70 | 0x3e | 0xb5 | 0x66 | 0x48 | 0x03 | 0xf6 | 0x0e | 0x61 | 0x35 | 0x57 | 0xb9 | 0x86 | 0xc1 | 0x1d | 0x9e |
E | 0xe1 | 0xf8 | 0x98 | 0x11 | 0x69 | 0xd9 | 0x8e | 0x94 | 0x9b | 0x1e | 0x87 | 0xe9 | 0xce | 0x55 | 0x28 | 0xdf |
F | 0x8c | 0xa1 | 0x89 | 0x0d | 0xbf | 0xe6 | 0x42 | 0x68 | 0x41 | 0x99 | 0x2d | 0x0f | 0xb0 | 0x54 | 0xbb | 0x16 |
状态矩阵中的元素按照下面的方式映射为一个新的字节:把该字节的高4位作为行值,低4位作为列值,取出S盒或者逆S盒中对应的行的元素作为输出。例如,加密时,输出的字节S1为0x12,则查S盒的第0x01行和0x02列,得到值0xc9,然后替换S1原有的0x12为0xc9。状态矩阵经字节代换后的图如下:
2.字节代换逆操作
逆字节代换也就是查逆S盒来变换,逆S盒如下:
行/列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0x52 | 0x09 | 0x6a | 0xd5 | 0x30 | 0x36 | 0xa5 | 0x38 | 0xbf | 0x40 | 0xa3 | 0x9e | 0x81 | 0xf3 | 0xd7 | 0xfb |
1 | 0x7c | 0xe3 | 0x39 | 0x82 | 0x9b | 0x2f | 0xff | 0x87 | 0x34 | 0x8e | 0x43 | 0x44 | 0xc4 | 0xde | 0xe9 | 0xcb |
2 | 0x54 | 0x7b | 0x94 | 0x32 | 0xa6 | 0xc2 | 0x23 | 0x3d | 0xee | 0x4c | 0x95 | 0x0b | 0x42 | 0xfa | 0xc3 | 0x4e |
3 | 0x08 | 0x2e | 0xa1 | 0x66 | 0x28 | 0xd9 | 0x24 | 0xb2 | 0x76 | 0x5b | 0xa2 | 0x49 | 0x6d | 0x8b | 0xd1 | 0x25 |
4 | 0x72 | 0xf8 | 0xf6 | 0x64 | 0x86 | 0x68 | 0x98 | 0x16 | 0xd4 | 0xa4 | 0x5c | 0xcc | 0x5d | 0x65 | 0xb6 | 0x92 |
5 | 0x6c | 0x70 | 0x48 | 0x50 | 0xfd | 0xed | 0xb9 | 0xda | 0x5e | 0x15 | 0x46 | 0x57 | 0xa7 | 0x8d | 0x9d | 0x84 |
6 | 0x90 | 0xd8 | 0xab | 0x00 | 0x8c | 0xbc | 0xd3 | 0x0a | 0xf7 | 0xe4 | 0x58 | 0x05 | 0xb8 | 0xb3 | 0x45 | 0x06 |
7 | 0xd0 | 0x2c | 0x1e | 0x8f | 0xca | 0x3f | 0x0f | 0x02 | 0xc1 | 0xaf | 0xbd | 0x03 | 0x01 | 0x13 | 0x8a | 0x6b |
8 | 0x3a | 0x91 | 0x11 | 0x41 | 0x4f | 0x67 | 0xdc | 0xea | 0x97 | 0xf2 | 0xcf | 0xce | 0xf0 | 0xb4 | 0xe6 | 0x73 |
9 | 0x96 | 0xac | 0x74 | 0x22 | 0xe7 | 0xad | 0x35 | 0x85 | 0xe2 | 0xf9 | 0x37 | 0xe8 | 0x1c | 0x75 | 0xdf | 0x6e |
A | 0x47 | 0xf1 | 0x1a | 0x71 | 0x1d | 0x29 | 0xc5 | 0x89 | 0x6f | 0xb7 | 0x62 | 0x0e | 0xaa | 0x18 | 0xbe | 0x1b |
B | 0xfc | 0x56 | 0x3e | 0x4b | 0xc6 | 0xd2 | 0x79 | 0x20 | 0x9a | 0xdb | 0xc0 | 0xfe | 0x78 | 0xcd | 0x5a | 0xf4 |
C | 0x1f | 0xdd | 0xa8 | 0x33 | 0x88 | 0x07 | 0xc7 | 0x31 | 0xb1 | 0x12 | 0x10 | 0x59 | 0x27 | 0x80 | 0xec | 0x5f |
D | 0x60 | 0x51 | 0x7f | 0xa9 | 0x19 | 0xb5 | 0x4a | 0x0d | 0x2d | 0xe5 | 0x7a | 0x9f | 0x93 | 0xc9 | 0x9c | 0xef |
E | 0xa0 | 0xe0 | 0x3b | 0x4d | 0xae | 0x2a | 0xf5 | 0xb0 | 0xc8 | 0xeb | 0xbb | 0x3c | 0x83 | 0x53 | 0x99 | 0x61 |
F | 0x17 | 0x2b | 0x04 | 0x7e | 0xba | 0x77 | 0xd6 | 0x26 | 0xe1 | 0x69 | 0x14 | 0x63 | 0x55 | 0x21 | 0x0c | 0x7d |
二、行移位
1.行移位操作
行移位是一个简单的左循环移位操作。当密钥长度为128比特时,状态矩阵的第0行左移0字节,第1行左移1字节,第2行左移2字节,第3行左移3字节,如下图所示:
2.行移位的逆变换
行移位的逆变换是将状态矩阵中的每一行执行相反的移位操作,例如AES-128中,状态矩阵的第0行右移0字节,第1行右移1字节,第2行右移2字节,第3行右移3字节。
三、列混合
1.列混合操作
列混合变换是通过矩阵相乘来实现的,经行移位后的状态矩阵与固定的矩阵相乘,得到混淆后的状态矩阵,如下图的公式所示:
状态矩阵中的第j列(0≤j≤3)的列混合可以表示为下图所示:
其中,矩阵元素的乘法和加法都是定义在基于GF(2^8)上的二元运算,并不是通常意义上的乘法和加法。这里涉及到一些信息安全上的数学知识,不过不懂这些知识也行。其实这种二元运算的加法等价于两个字节的异或,乘法则复杂一点。对于一个8位的二进制数来说,使用域上的乘法乘以(00000010)等价于左移1位(低位补0)后,再根据情况同(00011011)进行异或运算,设S1=(a7a6a5a4a3a2a1a0),刚0x02*S1如下图所示:
也就是说,如果a7为1,则进行异或运算,否则不进行。
类似地,乘以(00000100)可以拆分成两次乘以(00000010)的运算:
乘以(00000011)可以拆分成先分别乘以(00000001)和(00000010),再将两个乘积异或:
因此,我们只需要实现乘以2的函数,其他数值的乘法都可以通过组合来实现。
下面举个具体的例子,输入的状态矩阵如下:
C9 | E5 | FD | 2B |
7A | F2 | 78 | 6E |
63 | 9C | 26 | 67 |
B0 | A7 | 82 | E5 |
下面,进行列混合运算:
以第一列的运算为例:
其它列的计算就不列举了,列混合后生成的新状态矩阵如下:
D4 | E7 | CD | 66 |
28 | 02 | E5 | BB |
BE | C6 | D6 | BF |
22 | 0F | DF | A5 |
2.列混合逆运算
逆向列混合变换可由下图的矩阵乘法定义:
可以验证,逆变换矩阵同正变换矩阵的乘积恰好为单位矩阵。
四、轮密钥加
轮密钥加是将128位轮密钥Ki同状态矩阵中的数据进行逐位异或操作,如下图所示。其中,密钥Ki中每个字W[4i],W[4i+1],W[4i+2],W[4i+3]为32位比特字,包含4个字节,他们的生成算法下面在下面介绍。轮密钥加过程可以看成是字逐位异或的结果,也可以看成字节级别或者位级别的操作。也就是说,可以看成S0S1S2S3组成的32位字与W[4i]的异或运算。
轮密钥加的逆运算同正向的轮密钥加运算完全一致,这是因为异或的逆操作是其自身。轮密钥加非常简单,但却能够影响S数组中的每一位。
密钥扩展
AES首先将初始密钥输入到一个44的状态矩阵中,如下图所示。
这个44矩阵的每一列的4个字节组成一个字,矩阵4列的4个字依次命名为W[0]、W[1]、W[2]和W[3],它们构成一个以字为单位的数组W。例如,设密钥K为"abcdefghijklmnop",则K0=‘a',K1=‘b',K2=‘c',K3=‘d',W[0]=“abcd”。
接着,对W数组扩充40个新列,构成总共44列的扩展密钥数组。新列以如下的递归方式产生:
1.如果i不是4的倍数,那么第i列由如下等式确定:
W[i]=W[i-4]⨁W[i-1]
2.如果i是4的倍数,那么第i列由如下等式确定:
W[i]=W[i-4]⨁T(W[i-1])
其中,T是一个有点复杂的函数。
函数T由3部分组成:字循环、字节代换和轮常量异或,这3部分的作用分别如下。
a.字循环:将1个字中的4个字节循环左移1个字节。即将输入字[b0,b1,b2,b3]变换成[b1,b2,b3,b0]。
b.字节代换:对字循环的结果使用S盒进行字节代换。
c.轮常量异或:将前两步的结果同轮常量Rcon[j]进行异或,其中j表示轮数。
轮常量Rcon[j]是一个字,其值见下表。
j | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
Rcon[j] | 01000000 | 02000000 | 04000000 | 08000000 | 10000000 |
j | 6 | 7 | 8 | 9 | 10 |
Rcon[j] | 20000000 | 40000000 | 80000000 | 1B000000 | 36000000 |
下面举个例子:
设初始的128位密钥为:
3CA10B2157F01916902E1380ACC107BD
那么4个初始值为:
W[0]=3CA10B21
W[1]=57F01916
W[2]=902E1380
W[3]=ACC107BD
下面求扩展的第1轮的子密钥(W[4],W[5],W[6],W[7])。
由于4是4的倍数,所以:
W[4]=W[0]⨁T(W[3])
T(W[3])的计算步骤如下:
- 循环地将W[3]的元素移位:ACC107BD变成C107BDAC;
- 将C107BDAC作为S盒的输入,输出为78C57A91;
- 将78C57A91与第一轮轮常量Rcon[1]进行异或运算,将得到79C57A91,因此,T(W[3])=79C57A91,故
W[4]=3CA10B21⨁79C57A91=456471B0
其余的3个子密钥段的计算如下:
W[5]=W[1]⨁W[4]=57F01916⨁456471B0=129468A6
W[6]=W[2]⨁W[5]=902E1380⨁129468A6=82BA7B26
W[7]=W[3]⨁W[6]=ACC107BD⨁82BA7B26=2E7B7C9B
所以,第一轮的密钥为456471B0129468A682BA7B262E7B7C9B。
AES解密
在文章开始的图中,有AES解密的流程图,可以对应那个流程图来进行解密。下面介绍的是另一种等价的解密模式,流程图如下图所示。这种等价的解密模式使得解密过程各个变换的使用顺序同加密过程的顺序一致,只是用逆变换取代原来的变换。
AES原理到这里就结束了,下面主要为AES的实现,对以上原理中的每一个小节进行实现讲解,讲解的时候会插入一些关键代码,完整的代码参见文章最后。文章最后提供两个完整的程序,一个能在linux下面编译运行,一个能在VC6.0下面编译通过。
AES算法实现
AES加密函数预览
aes加密函数中,首先进行密钥扩展,然后把128位长度的字符串读进一个4*4的整数数组中,这个数组就是状态矩阵。例如,pArray[0][0]=S0,pArray[1][0]=S1,pArray[0][1]=S4。这个读取过程是通过convertToIntArray()函数来实现的。每个轮操作的函数都对pArray进行修改,也就是对状态矩阵进行混淆。在执行完10轮加密后,会把pArray转换回字符串,再存入明文p的字符数组中,所以,在加密完后,明文p的字符串中的字符就是加密后的字符了。这个转换过程是通过convertArrayToStr()函数来实现的。
/** *参数p:明文的字符串数组。 *参数plen:明文的长度。 *参数key:密钥的字符串数组。 */ voidaes(char*p,intplen,char*key){ intkeylen=strlen(key); if(plen==0||plen%16!=0){ printf("明文字符长度必须为16的倍数!\n"); exit(0); } if(!checkKeyLen(keylen)){ printf("密钥字符长度错误!长度必须为16、24和32。当前长度为%d\n",keylen); exit(0); } extendKey(key);//扩展密钥 intpArray[4][4]; for(intk=0;k1.密钥扩展的实现
在开始加密前,必须先获得第一轮加密用到的密钥,故先实现密钥扩展
下面是密钥扩展函数的实现,这个函数传入密钥key的字符串表示,然后从字符串中读取W[0]到W[3],函数getWordFromStr()用于实现此功能。读取后,就开始扩展密钥,当i是4的倍数的时候,就会调用T()函数来进行扩展,因为T函数的行为与加密的轮数有关,故要把加密的轮数j作为参数传进去。//密钥对应的扩展数组 staticintw[44]; /** *扩展密钥,结果是把w[44]中的每个元素初始化 */ staticvoidextendKey(char*key){ for(inti=0;i<4;i++) w[i]=getWordFromStr(key+i*4); for(inti=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()函数的代码实现,T()函数中接收两个参数,参数num为上面传进的W[i-1],round为加密的轮数。首先用一个numArray储存从32位的W[i-1]中取得4个字节。如果W[i-1]为0x12ABCDEF,那么numArray[0]=0x12,numArray[1]=0xAB。函数splitIntToArray()用于从32位整数中读取这四个字节,之所以这样做是因为整数数组比较容易操作。然后调用leftLoop4int()函数把numArray数组中的4个元素循环左移1位。然后执行字节代换,通过getNumFromSBox()函数来获取S盒中相应的值来替换numArray中的值。接着通过mergeArrayToInt()函数把字节代换后的numArray合并回32位的整数,在进行轮常量异或后返回。
/** *常量轮值表 */ staticconstintRcon[10]={0x01000000,0x02000000, 0x04000000,0x08000000, 0x10000000,0x20000000, 0x40000000,0x80000000, 0x1b000000,0x36000000}; /** *密钥扩展中的T函数 */ staticintT(intnum,intround){ intnumArray[4]; splitIntToArray(num,numArray); leftLoop4int(numArray,1);//字循环 //字节代换 for(inti=0;i<4;i++) numArray[i]=getNumFromSBox(numArray[i]); intresult=mergeArrayToInt(numArray); returnresult^Rcon[round]; }2.字节代换的实现
字节代换的代码很简单,就是把状态矩阵中的每个元素传进getNumFromSBox()函数中,然后取得前面8位中的高4位作为行值,低4位作为列值,然后返回S[row][col],这里的S是储存S盒的数组。
/** *根据索引,从S盒中获得元素 */ staticintgetNumFromSBox(intindex){ introw=getLeft4Bit(index); intcol=getRight4Bit(index); returnS[row][col]; } /** *字节代换 */ staticvoidsubBytes(intarray[4][4]){ for(inti=0;i<4;i++) for(intj=0;j<4;j++) array[i][j]=getNumFromSBox(array[i][j]); }3.行移位的实现
行移位的时候,首先把状态矩阵中第2,3,4行复制出来,然后对它们行进左移相应的位数,然后再复制回去状态矩阵array中。
/** *将数组中的元素循环左移step位 */ staticvoidleftLoop4int(intarray[4],intstep){ inttemp[4]; for(inti=0;i<4;i++) temp[i]=array[i]; intindex=step%4==0?0:step%4; for(inti=0;i<4;i++){ array[i]=temp[index]; index++; index=index%4; } } /** *行移位 */ staticvoidshiftRows(intarray[4][4]){ introwTwo[4],rowThree[4],rowFour[4]; //复制状态矩阵的第2,3,4行 for(inti=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(inti=0;i<4;i++){ array[1][i]=rowTwo[i]; array[2][i]=rowThree[i]; array[3][i]=rowFour[i]; } }4.列混合的实现
列混合函数中,先把状态矩阵初始状态复制一份到tempArray中,然后把tempArray与colM矩阵相乘,colM为存放要乘的常数矩阵的数组。其中的GFMul()函数定义了矩阵相乘时的乘法,加法则直接通过异或来实现。GFMul()通过调用乘以各个数对应的函数来实现乘法。例如,S1*2刚通过调用GFMul2(S1)来实现。S1*3刚通过GFMul3(S1)来实现。在这里,主要实现GFMul2()函数就行了,其它的都可以通过GFMul2()的组合来实现。举个例子吧,为计算下面这条等式,需要像下面这样调用函数
s=GFMul3(0xC9)^0x7A^0x63^GFMul2(0xB0)
/** *列混合要用到的矩阵 */ staticconstintcolM[4][4]={2,3,1,1, 1,2,3,1, 1,1,2,3, 3,1,1,2}; staticintGFMul2(ints){ intresult=s<<1; inta7=result&0x00000100; if(a7!=0){ result=result&0x000000ff; result=result^0x1b; } returnresult; } staticintGFMul3(ints){ returnGFMul2(s)^s; } /** *GF上的二元运算 */ staticintGFMul(intn,ints){ intresult; if(n==1) result=s; elseif(n==2) result=GFMul2(s); elseif(n==3) result=GFMul3(s); elseif(n==0x9) result=GFMul9(s); elseif(n==0xb)//11 result=GFMul11(s); elseif(n==0xd)//13 result=GFMul13(s); elseif(n==0xe)//14 result=GFMul14(s); returnresult; } /** *列混合 */ staticvoidmixColumns(intarray[4][4]){ inttempArray[4][4]; for(inti=0;i<4;i++) for(intj=0;j<4;j++) tempArray[i][j]=array[i][j]; for(inti=0;i<4;i++) for(intj=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]); } }5.轮密钥加的实现
轮密钥加的实现很简单,就是根据传入的轮数来把状态矩阵与相应的W[i]异或。
/** *轮密钥加 */ staticvoidaddRoundKey(intarray[4][4],intround){ intwarray[4]; for(inti=0;i<4;i++){ splitIntToArray(w[round*4+i],warray); for(intj=0;j<4;j++){ array[j][i]=array[j][i]^warray[j]; } } }AES解密函数
AES的解密函数和加密函数有点不同,可以参考上面的等价解密流程图来理解,解密函数中调用的是各轮操作的逆函数。逆函数在这里就不详细讲解了,可以参考最后的完整代码。
/** *参数c:密文的字符串数组。 *参数clen:密文的长度。 *参数key:密钥的字符串数组。 */ voiddeAes(char*c,intclen,char*key){ intkeylen=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);//扩展密钥 intcArray[4][4]; for(intk=0;k=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); } } 完整的程序代码
Linux版本
aes.h
#ifndefAES_H #defineAES_H /** *参数p:明文的字符串数组。 *参数plen:明文的长度,长度必须为16的倍数。 *参数key:密钥的字符串数组。 */ voidaes(char*p,intplen,char*key); /** *参数c:密文的字符串数组。 *参数clen:密文的长度,长度必须为16的倍数。 *参数key:密钥的字符串数组。 */ voiddeAes(char*c,intclen,char*key); #endifaes.c
#include#include #include #include"aes.h" /** *S盒 */ staticconstintS[16][16]={0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16}; /** *逆S盒 */ staticconstintS2[16][16]={0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb, 0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb, 0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e, 0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25, 0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92, 0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84, 0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06, 0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b, 0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73, 0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e, 0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b, 0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4, 0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f, 0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef, 0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61, 0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d}; /** *获取整形数据的低8位的左4个位 */ staticintgetLeft4Bit(intnum){ intleft=num&0x000000f0; returnleft>>4; } /** *获取整形数据的低8位的右4个位 */ staticintgetRight4Bit(intnum){ returnnum&0x0000000f; } /** *根据索引,从S盒中获得元素 */ staticintgetNumFromSBox(intindex){ introw=getLeft4Bit(index); intcol=getRight4Bit(index); returnS[row][col]; } /** *把一个字符转变成整型 */ staticintgetIntFromChar(charc){ intresult=(int)c; returnresult&0x000000ff; } /** *把16个字符转变成4X4的数组, *该矩阵中字节的排列顺序为从上到下, *从左到右依次排列。 */ staticvoidconvertToIntArray(char*str,intpa[4][4]){ intk=0; for(inti=0;i<4;i++) for(intj=0;j<4;j++){ pa[j][i]=getIntFromChar(str[k]); k++; } } /** *打印4X4的数组 */ staticvoidprintArray(inta[4][4]){ for(inti=0;i<4;i++){ for(intj=0;j<4;j++) printf("a[%d][%d]=0x%x",i,j,a[i][j]); printf("\n"); } printf("\n"); } /** *打印字符串的ASSCI, *以十六进制显示。 */ staticvoidprintASSCI(char*str,intlen){ for(inti=0;i >24; array[0]=one&0x000000ff; inttwo=num>>16; array[1]=two&0x000000ff; intthree=num>>8; array[2]=three&0x000000ff; array[3]=num&0x000000ff; } /** *将数组中的元素循环左移step位 */ staticvoidleftLoop4int(intarray[4],intstep){ inttemp[4]; for(inti=0;i<4;i++) temp[i]=array[i]; intindex=step%4==0?0:step%4; for(inti=0;i<4;i++){ array[i]=temp[index]; index++; index=index%4; } } /** *把数组中的第一、二、三和四元素分别作为 *4字节整型的第一、二、三和四字节,合并成一个4字节整型 */ staticintmergeArrayToInt(intarray[4]){ intone=array[0]<<24; inttwo=array[1]<<16; intthree=array[2]<<8; intfour=array[3]; returnone|two|three|four; } /** *常量轮值表 */ staticconstintRcon[10]={0x01000000,0x02000000, 0x04000000,0x08000000, 0x10000000,0x20000000, 0x40000000,0x80000000, 0x1b000000,0x36000000}; /** *密钥扩展中的T函数 */ staticintT(intnum,intround){ intnumArray[4]; splitIntToArray(num,numArray); leftLoop4int(numArray,1);//字循环 //字节代换 for(inti=0;i<4;i++) numArray[i]=getNumFromSBox(numArray[i]); intresult=mergeArrayToInt(numArray); returnresult^Rcon[round]; } //密钥对应的扩展数组 staticintw[44]; /** *扩展密钥,结果是把w[44]中的每个元素初始化 */ staticvoidextendKey(char*key){ for(inti=0;i<4;i++) w[i]=getWordFromStr(key+i*4); for(inti=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]; } } } /** *轮密钥加 */ staticvoidaddRoundKey(intarray[4][4],intround){ intwarray[4]; for(inti=0;i<4;i++){ splitIntToArray(w[round*4+i],warray); for(intj=0;j<4;j++){ array[j][i]=array[j][i]^warray[j]; } } } /** *字节代换 */ staticvoidsubBytes(intarray[4][4]){ for(inti=0;i<4;i++) for(intj=0;j<4;j++) array[i][j]=getNumFromSBox(array[i][j]); } /** *行移位 */ staticvoidshiftRows(intarray[4][4]){ introwTwo[4],rowThree[4],rowFour[4]; //复制状态矩阵的第2,3,4行 for(inti=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(inti=0;i<4;i++){ array[1][i]=rowTwo[i]; array[2][i]=rowThree[i]; array[3][i]=rowFour[i]; } } /** *列混合要用到的矩阵 */ staticconstintcolM[4][4]={2,3,1,1, 1,2,3,1, 1,1,2,3, 3,1,1,2}; staticintGFMul2(ints){ intresult=s<<1; inta7=result&0x00000100; if(a7!=0){ result=result&0x000000ff; result=result^0x1b; } returnresult; } staticintGFMul3(ints){ returnGFMul2(s)^s; } staticintGFMul4(ints){ returnGFMul2(GFMul2(s)); } staticintGFMul8(ints){ returnGFMul2(GFMul4(s)); } staticintGFMul9(ints){ returnGFMul8(s)^s; } staticintGFMul11(ints){ returnGFMul9(s)^GFMul2(s); } staticintGFMul12(ints){ returnGFMul8(s)^GFMul4(s); } staticintGFMul13(ints){ returnGFMul12(s)^s; } staticintGFMul14(ints){ returnGFMul12(s)^GFMul2(s); } /** *GF上的二元运算 */ staticintGFMul(intn,ints){ intresult; if(n==1) result=s; elseif(n==2) result=GFMul2(s); elseif(n==3) result=GFMul3(s); elseif(n==0x9) result=GFMul9(s); elseif(n==0xb)//11 result=GFMul11(s); elseif(n==0xd)//13 result=GFMul13(s); elseif(n==0xe)//14 result=GFMul14(s); returnresult; } /** *列混合 */ staticvoidmixColumns(intarray[4][4]){ inttempArray[4][4]; for(inti=0;i<4;i++) for(intj=0;j<4;j++) tempArray[i][j]=array[i][j]; for(inti=0;i<4;i++) for(intj=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]); } } /** *把4X4数组转回字符串 */ staticvoidconvertArrayToStr(intarray[4][4],char*str){ for(inti=0;i<4;i++) for(intj=0;j<4;j++) *str++=(char)array[j][i]; } /** *检查密钥长度 */ staticintcheckKeyLen(intlen){ if(len==16) return1; else return0; } /** *参数p:明文的字符串数组。 *参数plen:明文的长度。 *参数key:密钥的字符串数组。 */ voidaes(char*p,intplen,char*key){ intkeylen=strlen(key); if(plen==0||plen%16!=0){ printf("明文字符长度必须为16的倍数!\n"); exit(0); } if(!checkKeyLen(keylen)){ printf("密钥字符长度错误!长度必须为16、24和32。当前长度为%d\n",keylen); exit(0); } extendKey(key);//扩展密钥 intpArray[4][4]; for(intk=0;k =0;i--){ array[i]=temp[index]; index--; index=index==-1?3:index; } } /** *逆行移位 */ staticvoiddeShiftRows(intarray[4][4]){ introwTwo[4],rowThree[4],rowFour[4]; for(inti=0;i<4;i++){ rowTwo[i]=array[1][i]; rowThree[i]=array[2][i]; rowFour[i]=array[3][i]; } rightLoop4int(rowTwo,1); rightLoop4int(rowThree,2); rightLoop4int(rowFour,3); for(inti=0;i<4;i++){ array[1][i]=rowTwo[i]; array[2][i]=rowThree[i]; array[3][i]=rowFour[i]; } } /** *逆列混合用到的矩阵 */ staticconstintdeColM[4][4]={0xe,0xb,0xd,0x9, 0x9,0xe,0xb,0xd, 0xd,0x9,0xe,0xb, 0xb,0xd,0x9,0xe}; /** *逆列混合 */ staticvoiddeMixColumns(intarray[4][4]){ inttempArray[4][4]; for(inti=0;i<4;i++) for(intj=0;j<4;j++) tempArray[i][j]=array[i][j]; for(inti=0;i<4;i++) for(intj=0;j<4;j++){ array[i][j]=GFMul(deColM[i][0],tempArray[0][j])^GFMul(deColM[i][1],tempArray[1][j]) ^GFMul(deColM[i][2],tempArray[2][j])^GFMul(deColM[i][3],tempArray[3][j]); } } /** *把两个4X4数组进行异或 */ staticvoidaddRoundTowArray(intaArray[4][4],intbArray[4][4]){ for(inti=0;i<4;i++) for(intj=0;j<4;j++) aArray[i][j]=aArray[i][j]^bArray[i][j]; } /** *从4个32位的密钥字中获得4X4数组, *用于进行逆列混合 */ staticvoidgetArrayFrom4W(inti,intarray[4][4]){ intindex=i*4; intcolOne[4],colTwo[4],colThree[4],colFour[4]; splitIntToArray(w[index],colOne); splitIntToArray(w[index+1],colTwo); splitIntToArray(w[index+2],colThree); splitIntToArray(w[index+3],colFour); for(inti=0;i<4;i++){ array[i][0]=colOne[i]; array[i][1]=colTwo[i]; array[i][2]=colThree[i]; array[i][3]=colFour[i]; } } /** *参数c:密文的字符串数组。 *参数clen:密文的长度。 *参数key:密钥的字符串数组。 */ voiddeAes(char*c,intclen,char*key){ intkeylen=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);//扩展密钥 intcArray[4][4]; for(intk=0;k =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); } } main.c
#include#include #include #include #include"aes.h" #defineMAXLEN1024 voidgetString(char*str,intlen){ intslen=read(0,str,len); for(inti=0;i =MAXLEN){ printf("解密文件过大!\n"); exit(0); } str[i]='\0'; fclose(fp); returni; } voiddeAesFile(char*key){ charfileName[64]; charc[MAXLEN];//密文字符串 printf("请输入要解密的文件名,该文件必须和本程序在同一个目录\n"); if(scanf("%s",fileName)==1){ intclen=readStrFromFile(fileName,c); printf("开始解密.........\n"); deAes(c,clen,key); printf("解密后的明文ASCII为:\n"); printASCCI(c,clen); printf("明文为:%s\n",c); writeStrToFile(c,clen,fileName); printf("现在可以打开%s来查看解密后的密文了!\n",fileName); } } voidaesFile(char*key){ charfileName[64]; charfileP[MAXLEN]; printf("请输入要加密的文件名,该文件必须和本程序在同一个目录\n"); if(scanf("%s",fileName)==1){ readStrFromFile(fileName,fileP); intplen=strlen(fileP); printf("开始加密.........\n"); printf("加密前文件中字符的ASCII为:\n"); printASCCI(fileP,plen); aes(fileP,plen,key);//开始加密 printf("加密后的密文ASCII为:\n"); printASCCI(fileP,plen); writeStrToFile(fileP,plen,fileName); printf("已经将加密后的密文写进%s中了\n",fileName); } } intmain(intargc,charconst*argv[]){ charkey[17]; printf("请输入16个字符的密钥:\n"); intklen; while(1){ getString(key,17); klen=strlen(key); if(klen!=16){ printf("请输入16个字符的密钥,当前密钥的长度为%d\n",klen); }else{ printf("你输入的密钥为:%s\n",key); break; } } printf("输入's'表示要加密输入的字符串,并将加密后的内容写入到文件\n"); printf("请输入要功能选项并按回车,输入'f'表示要加密文件\n"); printf("输入'p'表示要解密文件\n"); charc; if(scanf("%s",&c)==1){ if(c=='s') aesStrToFile(key);//用AES加密字符串,并将字符串写进文件中 elseif(c=='p') deAesFile(key);//把文件中的密文解密,并写回文件中 elseif(c=='f')//用AES加密文件 aesFile(key); } return0; } 通过下面的gcc命令来编译运行:
gcc-oaesaes.cmain.cVC6.0版本
由于VC6.0的编译器比较坑,要先声明,后使用变量,故要对代码进行相应的修改。
aes.h
#ifndefMY_AES_H #defineMY_AES_H /** *参数p:明文的字符串数组。 *参数plen:明文的长度,长度必须为16的倍数。 *参数key:密钥的字符串数组。 */ voidaes(char*p,intplen,char*key); /** *参数c:密文的字符串数组。 *参数clen:密文的长度,长度必须为16的倍数。 *参数key:密钥的字符串数组。 */ voiddeAes(char*c,intclen,char*key); #endifaes.cpp
#include#include #include #include"aes.h" /** *S盒 */ staticconstintS[16][16]={0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16}; /** *逆S盒 */ staticconstintS2[16][16]={0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb, 0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb, 0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e, 0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25, 0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92, 0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84, 0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06, 0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b, 0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73, 0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e, 0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b, 0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4, 0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f, 0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef, 0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61, 0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d}; /** *获取整形数据的低8位的左4个位 */ staticintgetLeft4Bit(intnum){ intleft=num&0x000000f0; returnleft>>4; } /** *获取整形数据的低8位的右4个位 */ staticintgetRight4Bit(intnum){ returnnum&0x0000000f; } /** *根据索引,从S盒中获得元素 */ staticintgetNumFromSBox(intindex){ introw=getLeft4Bit(index); intcol=getRight4Bit(index); returnS[row][col]; } /** *把一个字符转变成整型 */ staticintgetIntFromChar(charc){ intresult=(int)c; returnresult&0x000000ff; } /** *把16个字符转变成4X4的数组, *该矩阵中字节的排列顺序为从上到下, *从左到右依次排列。 */ staticvoidconvertToIntArray(char*str,intpa[4][4]){ intk=0; inti,j; for(i=0;i<4;i++) for(j=0;j<4;j++){ pa[j][i]=getIntFromChar(str[k]); k++; } } /** *打印4X4的数组 */ staticvoidprintArray(inta[4][4]){ inti,j; for(i=0;i<4;i++){ for(j=0;j<4;j++) printf("a[%d][%d]=0x%x",i,j,a[i][j]); printf("\n"); } printf("\n"); } /** *打印字符串的ASSCI, *以十六进制显示。 */ staticvoidprintASSCI(char*str,intlen){ inti; for(i=0;i >24; array[0]=one&0x000000ff; two=num>>16; array[1]=two&0x000000ff; three=num>>8; array[2]=three&0x000000ff; array[3]=num&0x000000ff; } /** *将数组中的元素循环左移step位 */ staticvoidleftLoop4int(intarray[4],intstep){ inttemp[4]; inti; intindex; for(i=0;i<4;i++) temp[i]=array[i]; index=step%4== 声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。