700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 【图解AES加密算法】AES算法的Python实现 | Rijndael-128 | 对称加密 | 物联网安全

【图解AES加密算法】AES算法的Python实现 | Rijndael-128 | 对称加密 | 物联网安全

时间:2019-11-25 04:14:42

相关推荐

【图解AES加密算法】AES算法的Python实现 | Rijndael-128 | 对称加密 | 物联网安全

系列索引:【图解安全加密算法】加密算法系列索引 Python保姆级实现教程 | 物联网安全 | 信息安全

完整代码已更新

文章目录

一、AES的前世今生二、AES简介三、初始变换Initial round四、字节代换SubBytes五、行移位ShiftRows六、列混合MixColumns七、轮秘钥加AddRoundKey秘钥扩展八、流程回顾九、完整代码十、实验结果与心得体会

一、AES的前世今生

美国国家标准技术研究所在2001年发布了高级加密标准(AES)。AES是一个对称加密算法,旨在取代DES成为广泛使用的标准,该标准以Rijndael算法为核心

Rijndael算法是一种对称分组密码体制,采用代替或置换网络,每轮由三层组成

线性混合层确保多轮之上的高度扩散;非线性层由S盒并置起到混淆的作用;密钥加密层将子密钥异或到中间状态。

AES标准规定Rijndael算法的分组长度为128位,而密钥长度可以为128、192或256位,相应的迭代轮数为10轮、12轮或14轮。Rijndael 汇聚了安全性能、效率、可实现性和灵活性等优点。Rijndael 对内存的需求低,使它很适合用于资源受限制的环境中,Rijndael 的操作简单,并可抵御强大和实时的攻击

二、AES简介

AES,Advanced Encryption Standard,属于分组加密算法,明文长度固定位128位(16字节),秘钥长度可以是128,192,256位,分别循环10、12、14轮

其中字节是按照矩阵的形式进行排列

AES加密算法流程:

最终轮和9轮循环运算的区别是没有列混合。

三、初始变换Initial round

将明文转换后的结果和子秘钥矩阵进行异或运算得到处理结果

def Init_round(self, plaintext, key):self.plaintext_16 = np.array([_ for _ in plaintext.to_bytes(16, byteorder='big')]).reshape(4, 4).Tself.key_16 = np.array([_ for _ in key.to_bytes(16, byteorder='big')]).reshape(4, 4).Treturn self.plaintext_16 ^ self.key_16

四、字节代换SubBytes

字节代替:用一个S盒完成分组的字节到字节的代替;是一个基于S盒的非线性置换,它用于将每一个字节通过一个简单的查表操作映射为另一个字节。映射方法是把输入字节的高4位作为S盒的行值,低4位作为列值,然后取出S盒中对应行和列交叉位的元素作为输出

按照S盒尽心查询替换,这一步还是比较简单的,比如19->d4,是第1行第9列,查表可知结果为d4。

def SubBytes(self, arr):return np.array([self.S_BOX[i][j] for i, j in [(_ >> 4, _ & 0xF) for _ in np.nditer(arr, order='F')]]).reshape(4, 4)

五、行移位ShiftRows

行移位: AES 的行移位也是一个简单的左循环移位操作。当密钥长度为128比特时,状态矩阵的第0行左移0字节,第1行左移1字节,第2行左移2字节,第3行左移3字节

行移位也比较简单,就是第一行向左移动一个字节,以此类推

def ShiftRows(self, arr):S = np.zeros((4, 4), dtype='int')for i in range(arr.shape[0]):S[i] = np.roll(arr[i], -i)return S

六、列混合MixColumns

列混合:列混合变换是通过矩阵相乘来实现的,经行移位后的状态矩阵与固定的矩阵相乘,得到混淆后的状态矩阵。

将输入的4x4矩阵左乘一个给定的4x4矩阵

这里要区别于平常的矩阵乘法,不是线性代数中的相乘再相加

矩阵运算中的加法被替换为了异或操作

乘法运算也不一样,这里需要按字节进行运算,01等于本身,故只需要关注02,03,04,下面分别是其运算公式

def MixColumns(self, arr):M = np.zeros((4, 4), dtype=int)for row in range(4):for col in range(4):for i in range(4):M[row][col] ^= self.Mul(self.MIX_C[row][i], arr[i][col])return M

七、轮秘钥加AddRoundKey

轮密钥加:当前分组和扩展密钥的一部分进行按位异或,将输入或中间态S的每一列与一个密钥字ki进行按位异或,即将128位轮密钥 Ki 同状态矩阵中的数据进行逐位异或操作。

def AddRoundKey(self, arr, i):return arr ^ self.roundkey[:, (i-1)*4:(i-1)*4+4]

列混合得到的结果和轮秘钥进行异或运算,轮秘钥是由子秘钥矩阵扩展而来,下面来讲一下轮秘钥是怎么来的:

秘钥扩展

我们初始变换的到的是一个4x4的矩阵,比如我们接下来要求第i列,那么我们要看要求的这一列是否是4的倍数。

(1)如果i不是4的倍数,那么第i列由如下等式确定:

W[i]=W[i−4]⨁W[i−1]W[i]=W[i-4]⨁W[i-1] W[i]=W[i−4]⨁W[i−1]

(2)如果i是4的倍数,那么第i列由如下等式确定:

W[i]=W[i−4]⨁T(W[i−1])W[i]=W[i-4]⨁T(W[i-1]) W[i]=W[i−4]⨁T(W[i−1])

比如第5列(列数从0开始计),不是4的倍数,则用(1)中的公式计算即可,即第1列和第4列异或

当i是4的倍数时,要经过T函数的处理,T函数由三部分组成,分别是字循环字节代换轮常量异或

a.字循环

将1个字中的4个字节循环左移1个字节。即将输⼊字[b0, b1, b2, b3]变换成[b1,b2,b3,b0]。

b.字节代换

对字循环的结果使⽤S盒进⾏字节代换。

c.轮常量异或

将前两步的结果同轮常量Rcon[j]进⾏异或,其中j表示轮数。

这里的Rcon轮常量是给定的,与w[i-4]和字节代换后的结果进行异或运算得到最终的w[i]。

最终经过10轮的扩展得到轮秘钥,如下

def Round_key(self, arr):M = np.hstack((arr, np.zeros((4, 40), dtype=int)))for i in range(4, 44):M[:, i] = M[:, i-4] ^ M[:, i - 1] if i % 4 else M[:,i-4] ^ self.T(M[:, i-1], i//4-1)return M[:, 4:]

八、流程回顾

九、完整代码

这里仅给出加密过程,有能力的同学可以对照着写解密

import numpy as npclass AES:MIX_C = np.array([[0x2, 0x3, 0x1, 0x1], [0x1, 0x2, 0x3, 0x1], [0x1, 0x1, 0x2, 0x3], [0x3, 0x1, 0x1, 0x2]])RCon = np.array([[0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36],[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]])S_BOX = [[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, 0x1]]def __init__(self, plaintext, key):self.plaintext = plaintextself.key = keyself.plaintext_16 = Noneself.key_16 = Noneself.roundkey = Nonedef Init_round(self, plaintext, key):self.plaintext_16 = np.array([_ for _ in plaintext.to_bytes(16, byteorder='big')]).reshape(4, 4).Tself.key_16 = np.array([_ for _ in key.to_bytes(16, byteorder='big')]).reshape(4, 4).Treturn self.plaintext_16 ^ self.key_16def SubBytes(self, arr):return np.array([self.S_BOX[i][j] for i, j in [(_ >> 4, _ & 0xF) for _ in np.nditer(arr, order='F')]]).reshape(4, 4)def ShiftRows(self, arr):S = np.zeros((4, 4), dtype='int')for i in range(arr.shape[0]):S[i] = np.roll(arr[i], -i)return Sdef Mul(self, x1, x2):if x1 == 0x1:x = x2elif x1 == 0x2:x = ((x2 << 1 & 0xff) ^ 0b00011011) if (x2 & 0x80) else (x2 << 1 & 0xff)elif x1 == 0x3:x = self.Mul(0x2, x2) ^ x2elif x1 == 0x4:x = self.Mul(Mul(0x2, 0x2), x2)return xdef MixColumns(self, arr):M = np.zeros((4, 4), dtype=int)for row in range(4):for col in range(4):for i in range(4):M[row][col] ^= self.Mul(self.MIX_C[row][i], arr[i][col])return Mdef T(self, arr, i):arr = np.roll(arr, -1)M = np.array([self.S_BOX[i][j] for i, j in [(_ >> 4, _ & 0xF) for _ in arr]])M = M ^ self.RCon[:, i]return Mdef Round_key(self, arr):M = np.hstack((arr, np.zeros((4, 40), dtype=int)))for i in range(4, 44):M[:, i] = M[:, i-4] ^ M[:, i - 1] if i % 4 else M[:,i-4] ^ self.T(M[:, i-1], i//4-1)return M[:, 4:]def AddRoundKey(self, arr, i):return arr ^ self.roundkey[:, (i-1)*4:(i-1)*4+4]def aes_encrypt(self):State = self.Init_round(self.plaintext, self.key)self.roundkey = self.Round_key(self.key_16)for r in range(1, 10):State = self.SubBytes(State)State = self.ShiftRows(State)State = self.MixColumns(State)State = self.AddRoundKey(State, r)State = self.SubBytes(State)State = self.ShiftRows(State)State = self.AddRoundKey(State, 10)return Stateif __name__ == '__main__':plaintext = 0x00112233445566778899aabbccddeeffkey = 0x000102030405060708090a0b0c0d0e0faes = AES(plaintext, key)print(aes.aes_encrypt())# print(arr)# print(aes.ShiftRows(arr))# print(aes.SubBytes(arr))# arr = np.array([[0xd4, 0xe0, 0xb8, 0x1e], [0xbf, 0xb4, 0x41, 0x27], [0x5d, 0x52, 0x11, 0x98], [0x30, 0xae, 0xf1, 0xe5]])# print(aes.MixColumns(arr))# arr= np.array([[0x2b, 0x28, 0xab, 0x09], [0x7e, 0xae, 0xf7, 0xcf], [0x15, 0xd2, 0x15, 0x4f], [0x16, 0xa6, 0x88, 0x3c]])# print(aes.Round_key(arr))

十、实验结果与心得体会

将结果储存在了一个矩阵里,结果用int型表示。(这个实验是这学期第一次写加密算法,所以代码写的比较稚嫩,各位大佬见谅,中间变量没有处理成二进制或16进制,大佬可在此基础上改进)

心得:

不得不说,AES理论理解起来并不是特别难,但当真正去动手复现算法,确是一头雾水,在机房的时候感觉无从下手,回去之后又把课本认真看了看,搞清楚了各个步骤之间的衔接和逻辑,然后开始了代码设计,我选择的是Python,因为了解过一些用于科学计算的库,这里我就使用了numpy。这个实验我真是从0开始一行一行实现各个函数的功能,其间的坎坷让我加深了对AES的理解,先是把各个步骤的代码实现并进行验证,然后再写一个函数将他们串联起来,这个实验我写了两天,虽然中间也有其他事情要处理,不过当最后完整验算过一遍之后,内心十分开心,觉得努力没有白费!

图解安全加密算法系列持续更新,欢迎点赞收藏关注

上一篇:【图解DSA数字签名算法】DSA签名算法的Python实现 | 物联网安全 | 信息安全

下一篇:【图解RSA加密算法】RSA非对称密码算法的Python实现保姆级教程 | 物联网安全 | 信息安全

本人水平有限,文章中不足之处欢迎下方👇评论区批评指正~

如果感觉对你有帮助,点个赞👍 支持一下吧 ~

不定期分享 有趣、有料、有营养内容,欢迎 订阅关注 🤝 我的博客 ,期待在这与你相遇 ~

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。