一、算法(Rijndael)
AES 属于分组加密算法
明文长度固定为128位(16字节)
密钥长度可以是128(循环10次)、192(循环12次)、256位(循环14次)
二、加密
输入明文和密钥的字节顺序:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
规定字节的排列方式:
1 | 5 | 9 | 13 |
2 | 6 | 10 | 14 |
3 | 7 | 11 | 15 |
4 | 8 | 12 | 16 |
总加密过程:
注意:第十轮(最终轮)循环运算中没有列混合
分步加密:
1.初始变换:
明文矩阵和子密钥矩阵进行异或
2.字节代换
字节代换是非线性变换,独立地对状态的每个字节进行。代换表(S-Box)是可逆的。
相当于明文在s盒中进行行列的映射。
S盒
eg:
19 | a0 | 9a | e9 | → | d4 | e0 | b8 | 1e |
3d | f4 | c6 | f8 | → | 27 | bf | b4 | 41 |
e3 | e2 | 8d | 48 | → | 11 | 98 | 5d | 52 |
be | 2b | 2a | 08 | → | ae | f1 | e5 | 30 |
说明:19代表第一行第九列,在S盒中对应0*d4;3d代表第三行第d列,在S盒中对应0*27······以此类推。
3.行移位
eg:
d4 | e0 | b8 | 1e | → | d4 | e0 | b8 | 1e |
27 | bf | b4 | 41 | → | bf | b4 | 41 | 27 |
11 | 98 | 5d | 52 | → | 5d | 52 | 11 | 98 |
ae | f1 | e5 | 30 | → | 30 | ae | f1 | e5 |
4.列混合
将输⼊的4*4的矩阵左乘⼀个规定的4*4正矩阵
eg:
02 | 03 | 01 | 01 | d4 | 04 | ||
01 | 02 | 03 | 01 | * | bf | = | 66 |
01 | 01 | 02 | 03 | 5d | 81 | ||
03 | 01 | 01 | 02 | 30 | e5 |
5.轮密钥加
6.轮密钥的生成
w[0] | w[1] | w[2] | w[3] | w[4] | w[5] | w[6] | w[7] | |
---|---|---|---|---|---|---|---|---|
2b | 28 | ab | 09 | |||||
7e | ae | f7 | cf | |||||
15 | d2 | 15 | 4f | |||||
16 | a6 | 88 | 3c |
1.密钥扩展(i不是4的倍数)
2.密钥扩展(i是4的倍数)
T函数由3部分组成:字循环,字节代换和轮常量异或。
a.字循环:将w[3]中的4个字节循环左移1个字节。即,将输入字[k1,k2,k3,k4]变换成
[k2,k3,k4,k1].
09 | → | cf |
cf | → | 4f |
4f | → | 3c |
3c | → | 09 |
b.字节代换:对字循环的结果使用s盒进行字节代换
cf | → | 8a |
4f | → | 84 |
3c | → | eb |
09 | → | 01 |
c.轮常量异或:将前两步的结果同轮常量Rcon[j]进行异或,其中j表示轮数。
轮常量异或表 | |||||||||
---|---|---|---|---|---|---|---|---|---|
01 | 02 | 04 | 08 | 10 | 20 | 40 | 80 | 1b | 36 |
00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
得到T(w[3]),最后再和W[0]异或
最终通过计算生成10轮密钥
7.python脚本实现
在Python中,可以使用pycryptodome
库来实现AES加密。pycryptodome
是PyCrypto
的一个分支,提供了更全面的加密算法支持。
安装pycryptodome
首先,需要安装pycryptodome
库。
案例一:使用CBC模式和PKCS#7填充
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes
import base64
# 生成随机的密钥(必须是16、24或32字节)
key = get_random_bytes(16)
# 生成随机的初始化向量(IV),AES块大小为16字节
iv = get_random_bytes(AES.block_size)
# 明文数据
plain_text = "Hello, AES Encryption!".encode('utf-8')
# 使用PKCS#7填充明文数据到AES块大小的倍数
padded_data = pad(plain_text, AES.block_size)
# 创建AES加密器,使用CBC模式和之前生成的密钥及IV
cipher = AES.new(key, AES.MODE_CBC, iv)
# 加密填充后的数据
encrypted_data = cipher.encrypt(padded_data)
# 将IV和密文拼接,然后Base64编码以便于存储或传输
cipher_text_str = base64.b64encode(iv + encrypted_data).decode('utf-8')
print("加密后的数据:", cipher_text_str)
# 解密过程
# 创建相同的AES解密器
decrypt_cipher = AES.new(key, AES.MODE_CBC, iv)
# 解密数据
decrypted_padded_data = decrypt_cipher.decrypt(encrypted_data)
# 去除填充
decrypted_text = unpad(decrypted_padded_data, AES.block_size)
print("解密后的数据:", decrypted_text.decode('utf-8'))
输出结果:
加密后的数据: UYQTIYCyXTclVckVAmQH3VjWjfZLhblZGNTkNe2CSIrxBwcbXRutES9I+Elid4Hj
解密后的数据: Hello, AES Encryption!
3、案例二:使用ECB模式和自定义填充
注意:ECB模式不推荐使用于加密长数据或敏感数据,因为它不使用IV且相同的明文块会产生相同的密文块。
from Crypto.Cipher import AES
import base64
from Crypto.Random import get_random_bytes
# 生成随机的密钥(必须是16、24或32字节)
key = get_random_bytes(16)
# 明文数据
plain_text = "ECB模式进行AES加密".encode('utf-8')
# 自定义填充函数(这里使用简单的'X'字符填充)
def custom_pad(s, block_size=AES.block_size):
pad_len = block_size - len(s) % block_size
return s + b'h' * pad_len
# 加密
cipher = AES.new(key, AES.MODE_ECB)
padded_data = custom_pad(plain_text)
encrypted_data = cipher.encrypt(padded_data)
# Base64编码
cipher_text_str = base64.b64encode(encrypted_data).decode('utf-8')
print("加密后输出:", cipher_text_str)
# 解密(同样需要自定义填充)
decrypted_data = cipher.decrypt(encrypted_data)
# 去除填充(注意:这里需要知道填充的规则)
decrypted_text = decrypted_data.rstrip(b'h').decode('utf-8')
print("解密后输出:", decrypted_text)
输出结果:
加密后输出: ZYUTsU3bZdWF7pq4pFA1J/mbR4DRLMHUMcoOYhODcNM=
解密后输出: ECB模式进行AES加密
3、案例三:使用GCM模式
GCM(Galois/Counter Mode)是一种结合了CTR模式和GHASH(Galois Hash)的认证加密模式,它提供了数据的机密性和完整性保护。
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad
import base64
# 生成随机的密钥(对于AES-GCM,推荐使用128位(16字节)或256位(32字节)的密钥)
key = get_random_bytes(32) # 生成一个32字节(256位)的密钥
# 生成随机的nonce(GCM中的IV被称为nonce,不需要保密,但必须确保唯一)
nonce = get_random_bytes(16) # 生成一个16字节的nonce
# 明文数据
plain_text = "AES-GCM加密!".encode('utf-8')
# AES-GCM不需要传统的填充,但我们可以使用pad函数以确保与可能的其他加密模式兼容(尽管在这里是多余的)
# 注意:对于GCM,实际上应该直接加密原始明文,不需要填充
# 这里为了展示pad函数的使用(虽然在这个上下文中是多余的),我们还是使用它
padded_data = pad(plain_text, AES.block_size) # 但GCM模式下这步是多余的
# 由于GCM模式下不需要填充,我们直接加密原始明文
# 创建AES-GCM加密器
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
# 加密数据并获取密文和认证标签
ciphertext, tag = cipher.encrypt_and_digest(plain_text) # 加密明文并获取密文和GCM认证标签
# 通常,你会将nonce、密文和标签一起发送给接收者(或存储它们以供以后使用)
# 这里,为了简化,我们将它们Base64编码并打印出来
cipher_text_with_nonce_and_tag = base64.b64encode(nonce + ciphertext + tag).decode('utf-8')
print("加密后输出:", cipher_text_with_nonce_and_tag)
# 解密过程
# 接收方需要拥有相同的密钥和nonce来解密和验证密文
decrypt_cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
# 解密数据并验证认证标签
try:
decrypted_text = decrypt_cipher.decrypt_and_verify(ciphertext, tag)
print("解密后输出:", decrypted_text.decode('utf-8'))
except ValueError:
# 如果认证标签不匹配,decrypt_and_verify将抛出一个ValueError
print("身份验证失败!.")
# 注意:在实际应用中,nonce(IV)和密钥都应该安全地存储和传输
# 密钥应该保密,而nonce(IV)应该对每个加密操作都是唯一的,但不需要保密
# 认证标签(tag)是验证密文完整性和真实性的关键部分,必须随密文一起发送或存储
输出结果:
加密后输出:FwPwETBk5pxUyKPQ8iOQReBHUS2inee//aaKDlumHoj32olJRqMLTWaaWImJwQ==
解密后输出: AES-GCM加密!