python实现AES加密
Frey November 13, 2022 [算法] #AES #Python #加密摘要:作为新一代的加密标准,AES 旨在取代 DES,以适应当今分布式开放网络对数据加密安全性的要求。 本文在分析了 AES 加密原理的基础上着重说明了算法实现的具体步骤,并用 python 实现了对字符串的加密和解密。
AES介绍
AES(高级加密标准,Advanced Encryption Standard),在密码学中又称 Rijndael 加密法,是美国联邦政府采用的一种分组加密标准。这个标准用来替代原先的 DES,目前已经广为全世界所使用,成为对称密钥算法中最流行的算法之一。
在 AES 出现之前,最常用的对称密钥算法是 DES 加密算法,它在 1977 年被公布成为美国政府的商用加密标准。 DES 的主要问题是密钥长度较短,渐渐不适合于分布式开放网络对数据加密安全性的要求。 因此,1998年美国政府决定不再继续延用 DES 作为联邦加密标准,并发起了征集 AES 候选算法的活动。 征集活动对 AES 的基本要求是: 比三重DES快、至少与三重DES一样安全、数据分组长度为128比特、密钥长度为128/192/256比特。
经过三年多的甄选,比利时的密码学家所设计的 Rijndael 算法最终脱颖而出,成为新一代的高级加密标准,并于 2001 年由美国国家标准与技术研究院(NIST)发布于 FIPS PUB 197。
AES算法原理
AES算法(即 Rijndael 算法)是一个对称分组密码算法。 数据分组长度必须是 128 bits,使用的密钥长度为 128,192 或 256 bits。 对于三种不同密钥长度的 AES 算法,分别称为“AES-128”、“AES-192”、“AES-256”。 (Rijndael 的设计还可以处理其它的分组长度和密钥长度,但 AES 标准中没有采用)
下图是 AES 加密解密的整体流程图:
这里我们需要知道3个符号:
- Nb: 状态 State 包含的列(32-bit 字)的个数,也就是说 Nb=4;
- Nk: 密钥包含的 32-bit 字的个数,也就是说 Nk=4,6 或 8;
- Nr: 加密的轮数,对于不同密钥长度,轮数不一样,具体如下表所示:
密钥长度(Nk words) | 分组大小(Nb words) | 轮数(Nr) | |
---|---|---|---|
AES-128 | 4 | 4 | 10 |
AES-192 | 6 | 4 | 12 |
AES-256 | 8 | 4 | 14 |
下面分三个部分对AES加密解密流程进行梳理
密钥扩展
AES 算法通过密钥扩展程序(Key Expansion)将用户输入的密钥K
扩展生成Nb(Nr+1)
个字,存放在一个线性数组w[Nb*(Nr+1)]
中。具体如下:
- 位置变换函数
RotWord()
,接受一个字[a0, a1, a2, a3]
作为输入,循环左移一个字节后输出[a1, a2, a3, a0]
。 - S盒变换函数
SubWord()
,接受一个字[a0, a1, a2, a3]
作为输入。S盒是一个16x16的表,其中每一个元素是一个字节。对于输入的每一个字节,前四位组成十六进制数x
作为行号,后四位组成的十六进制数y
作为列号,查找表中对应的值。最后函数输出 4 个新字节组成的 32-bit 字。 - 轮常数
Rcon[]
,如何计算的就不说了,直接把它当做常量数组。 扩展密钥数组w[]
的前Nk
个元素就是外部密钥K
,后的元素w[i]
等于它前一个元素w[i-1]
与前第Nk
个元素w[i-Nk]
的异或,即w[i] = w[i-1] XOR w[i-Nk]
;但若i
为Nk
的倍数,则w[i] = w[i-Nk] XOR SubWord(RotWord(w[i-1])) XOR Rcon[i/Nk-1]
。
注意,上面的第四步说明仅适合于 AES-128 和 AES-192;
加密
AES加密过程涉及到4种变换:S盒变换,行变换,列变换,与扩展密钥的异或
S盒变换-SubBytes()
在密钥扩展部分已经讲过了,S盒是一个 16 行 16 列的表,表中每个元素都是一个字节。S盒变换很简单:函数SubBytes()
接受一个 4x4 的字节矩阵作为输入,对其中的每个字节,前四位组成十六进制数 x 作为行号,后四位组成的十六进制数 y 作为列号,查找表中对应的值替换原来位置上的字节。
行变换-ShiftRows()
行变换也很简单,它仅仅是将矩阵的每一行以字节为单位循环移位:第一行不变,第二行左移一位,第三行左移两位,第四行左移三位。如下图所示:
与扩展密钥的异或-AddRoundKey()
扩展密钥只参与了这一步。根据当前加密的轮数,用w[]
中的 4 个扩展密钥与矩阵的 4 个列进行按位异或。如下图:
解密
解密需要分别实现 S 盒变换、行变换和列变换的逆变换InvShiftRows(),InvSubBytes(),InvMixColumns()
python实现
=
=
# 轮常数,密钥扩展中用到。(AES-128只需要10轮)
=
# 密钥扩展
# 字循环左移一个字节
return & 0xffffffff
# S盒变换
= 0
= |
= |
= |
= |
return
# 加密过程
# 轮密钥加变换 - 将每一列与扩展密钥进行异或
=
return
# S盒变换 - 前4位为行号,后4位为列号
=
return
# 行变换 - 按字节循环移位
= b
+= + \
+ + \
+ + \
+
return
# 有限域上的乘法 GF(2^8)
, = 0, 0
^=
= & 0x80
= & 0xff
^= 0x1b # x^8 + x^4 + x^3 + x + 1
>>= 1
return & 0xff
# 列变换
= * 16
=
= ^ ^ ^
= ^ ^ ^
= ^ ^ ^
= ^ ^ ^
return
# 解密过程
# 逆行变换 - 按字节循环移位
= b
+= + \
+ + \
+ + \
+
return
# 逆S盒变换 - 前4位为行号,后4位为列号
=
return
# 逆列变换
= * 16
=
= ^ ^ ^
= ^ ^ ^
= ^ ^ ^
= ^ ^ ^
return
# AES-128
# Nk密钥长度(双字),Nb分组大小(双字),Nr轮数
# 128 4 4 10
# 192 6 4 12
# 256 8 4 14
# 128 4*4*8=128bits 4*4*8=128bits 10轮
, , = 4, 4, 10
# 通过密钥K生成实例,密钥K位数不足补零
=
+= b
=
# 加密
# 明文不足16*8bits补零
+= b
= b
# 对每一个16*8bits的块进行循环
=
=
=
=
=
=
=
=
=
=
=
=
+=
return
# 解密
= b
=
=
=
=
=
=
=
=
=
=
=
=
+=
return
# 密钥扩展函数,密钥 K 扩展生成 Nb(Nr+1)个字 4*4*8bits=128bits -> 4*(10+1)*32bits=1408bits
=
= 0
= |
=
= ^
=
return
# 返回密钥K
return
# 随机产生密钥K
pass
# AES-128
# 密钥不足128bits 添零,多余128bits 截取前128bits
# messages 不足128bits的倍数 补零
= b
= b
=
=
=
# 以bytes输出