-
-
[分享]AES加密算法
-
发表于: 2021-9-16 16:15 19736
-
菜鸡本菜。学习密码学才发现数学多么重要,孩子都要哭了。
本文仅个人的学习笔记,非常简略。
在此非常感谢QiuJYu的文章,各位如果想知道更加详细的AES加密,可以看看大佬的这篇文章
简介
- 密码类型:分组对称加密
- 明文长度:128bits
- 密文长度:128bits
- 密钥长度:128bits
AES(英语:Advanced Encryption Standard,缩写:AES),即高级加密标准,在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。
加密流程
AES算法主要有四种操作处理,分别是
- 密钥加法层 (也叫轮密钥加,英文Add Round Key)
- 字节代换层 (SubByte)
- 行位移层 (Shift Rows)
- 列混淆层(Mix Column)

AES算法在处理的轮数上只有最后一轮操作与前面的轮处理上有些许不同(最后一轮只是少了列混淆处理),在轮处理开始前还单独进行了一次轮密钥加的处理。在处理轮数上,我们只考虑128位密钥的10轮处理。
AES算法的明文和密钥都是由16个字节组成的数据,它是按照字节的先后顺序从上到下、从左到右进行排列的。而加密出的密文读取顺序也是按照这个顺序读取的,相当于将数组还原成字符串的模样了,然后再解密的时候又是按照4x4数组处理的。
密钥加法层
在密钥加法层中有两个输入的参数,分别是明文和子密钥k[0],而且这两个输入都是128位的.k[0]实际上就等同于密钥k.只需要将两个输入的数据进行按字节异或操作就会得到运算的结果。

相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | int AddRoundKey(unsigned char(*PlainArray)[4], unsigned char(*ExtendKeyArray)[44], unsigned int MinCol){ int ret = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { PlainArray[i][j] ^= ExtendKeyArray[i][MinCol + j]; } } return ret;} |
字节代换层
字节代换层的主要功能就是让输入的数据通过S_box表完成从一个字节到另一个字节的映射;S盒是16×16个字节组成的矩阵,行列的索引值分别从0开始,到十六进制的F结束,每个字节的范围为(00-FF)。
s盒

读取S_box数据的方法就是要将输入数据的每个字节的高四位作为第一个下标,第四位作为第二个下标

加密图示:

相关代码:
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 | //S盒const unsigned char S_Table[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};//字节代换int Plain_S_Substitution(unsigned char *PlainArray){ int ret = 0; for (int i = 0; i < 16; i++) { PlainArray[i] = S_Table[PlainArray[i] >> 4][PlainArray[i] & 0x0F]; } return ret;} |
行位移
行移位是一个4x4的矩阵内部字节之间的置换,用于提供算法的扩散性。
正向行移位用于加密,其原理图如下。其中:第一行保持不变,第二行循环左移8比特,第三行循环左移16比特,第四行循环左移24比特。

对应代码(这里将char二维数组强制转换成int一维数组处理):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | int ReShiftRows(unsigned int *CipherArray){ int ret = 0; //第一行 不移位 //CipherArray[0] = CipherArray[0]; //第二行 右移8Bit CipherArray[1] = (CipherArray[1] << 8) | (CipherArray[1] >> 24); //第三行 右移16Bit CipherArray[2] = (CipherArray[2] << 16) | (CipherArray[2] >> 16); //第四行 右移24Bit CipherArray[3] = (CipherArray[3] << 24) | (CipherArray[3] >> 8); return ret;} |
列混淆层
在加密的正向列混淆中,我们要将输入的4·4矩阵左乘一个给定的4·4矩阵。
根据矩阵的乘法可知,在列混淆(利用域GF(28)上的算术特性的一个代替)的过程中,每个字节对应的值只与该列的4个值有关系。此处的乘法和加法需要注意如下几点:
(1)将某个字节所对应的值乘以2,其结果就是将该值的二进制位左移一位,如果该值的最高位为1(表示该数值不小于128),则还需要将移位后的结果异或00011011
(2)乘法对加法满足分配率,例如:07·S0,0=(01⊕02⊕04)·S0,0= S0,0⊕(02·S0,0)(04·S0,0)
(3)此处的矩阵乘法与一般意义上矩阵的乘法有所不同,各个值在相加时使用的是模2加法(异或运算)。
密钥扩展

密钥扩展过程说明:
1) 将种子密钥按图(a)的格式排列,其中k0、k1、……、k15依次表示种子密钥的一个字节;排列后用4个32比特的字表示,分别记为w[0]、w[1]、w[2]、w[3];
2) 按照如下方式,依次求解w[j],其中j是整数并且属于[4,43];
3) 若j%4=0,则w[j]=w[j-4]⊕g(w[j-1]),否则w[j]=w[j-4]⊕w[j-1];
函数g的流程说明:
a) 将w循环左移8比特;
b) 分别对每个字节做S盒置换;
c) 与32比特的常量(RC[j/4],0,0,0)进行异或,RC是一个一维数组,其值如下。(RC的值只需要有10个,而此处用了11个,实际上RC[0]在运算中没有用到,增加RC[0]是为了便于程序中用数组表示。由于j的最小取值是4,j/4的最小取值则是1,因此不会产生错误。)
RC = {0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36}
AES解密流程

解密阶段与加密阶段相似,但有几处参数或顺序不同:
- 逆字节代换层 S-Box 变为 逆S-Box
逆S-Box
- 逆向行位移层 左移变为右移动

- 逆列混淆层 左乘矩阵变为下面这样

逆向特征
S-Box和逆S-Box数据

利用python进行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 | #AES-demoimport base64from Crypto.Cipher import AES'''采用AES对称加密算法'''# str不是16的倍数那就补足为16的倍数def add_to_16(value): while len(value) % 16 != 0: value += '\0' return str.encode(value) # 返回bytes#加密方法def encrypt_oracle(): # 秘钥 key = '123456' # 待加密文本 text = 'abc123def456' # 初始化加密器 aes = AES.new(add_to_16(key), AES.MODE_ECB) #先进行aes加密 encrypt_aes = aes.encrypt(add_to_16(text)) #用base64转成字符串形式 encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8') # 执行加密并转码返回bytes print(encrypted_text)#解密方法def decrypt_oralce(): # 秘钥 key = '123456' # 密文 text = 'qR/TQk4INsWeXdMSbCDDdA==' # 初始化加密器 aes = AES.new(add_to_16(key), AES.MODE_ECB) #优先逆向解密base64成bytes base64_decrypted = base64.decodebytes(text.encode(encoding='utf-8')) #执行解密密并转码返回str decrypted_text = str(aes.decrypt(base64_decrypted),encoding='utf-8').replace('\0','') print(decrypted_text)if __name__ == '__main__': # encrypt_oracle() decrypt_oralce() |
参考
[培训]传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
- Android主流的污点分析工具 1874
- [翻译]“过滤-静音”操作:调查EDR内部通信 13403
- [翻译]Windows授权指南 6802
- [翻译]利用Android WebView漏洞 8822
- [翻译]伪造调用堆栈以混淆EDR 14120