活到老学到老之AES加密
AES简介
AES(Advanced Encryption Standard,高级加密标准)是一种对称加密算法,广泛应用于数据加密与传输安全。AES 是美国国家标准与技术研究院(NIST)在 2001 年正式发布的标准,取代了之前的 DES(Data Encryption Standard)。
特性 | 描述 |
---|---|
加密方式 | 对称加密(加密和解密使用同一个密钥) |
加密算法 | 分组加密(block cipher) |
分组长度 | 固定为 128 位(16 字节) |
密钥长度 | 128 位、192 位 或 256 位 |
加密轮数 | 根据密钥长度不同,分别为 10/12/14 轮 |
安全性 | 目前仍被认为是高度安全的 |
应用 | HTTPS、VPN、文件加密、磁盘加密、数据库加密等 |
AES原理
AES 把明文(plaintext)数据按块(block)加密,每块为 128 位(16 字节),然后执行若干轮如下操作:
- 字节替代(SubBytes):使用一个称为 S-Box 的替代表,对每个字节进行替换。
- 行移位(ShiftRows):对每行字节进行循环移位操作。
- 列混淆(MixColumns):在每一列内做数学变换,增加混淆。
- 轮密钥加(AddRoundKey):将每轮生成的密钥与当前状态进行异或操作。
每轮都会进行这些操作,最后一轮略去列混淆。
密钥扩展
AES 并不直接使用初始密钥进行每一轮加密,而是先把初始密钥扩展成多个轮密钥(Round Keys),每轮用一个。这个过程称为密钥扩展(Key Expansion)。
加密模式(Mode of Operation)
AES 本身只能加密固定长度的块(16 字节),为了解决加密多块数据的问题,引入了多种加密模式,例如:
模式 | 描述 |
---|---|
ECB | 每块独立加密,不安全,易遭重放攻击 |
CBC | 每块与前一块加密结果异或 |
CFB | 反馈方式,适合流式加密 |
OFB | 输出反馈模式,适合流式加密 |
CTR | 将计数器加密后与明文异或,支持并行 |
GCM | 加密同时提供认证(AEAD) |
优点
- 安全性高(目前无有效破解方法)
- 运行速度快,适合硬件和软件实现
- 被广泛采用(FIPS、TLS、IPSec、Disk Encryption 等)
PKCS7
PKCS7 是一种 填充(padding)算法,常用于对称加密算法(如 AES)中,以确保明文数据的长度满足加密算法对块大小(block size)的要求。就是说PKCS7 是为了让数据长度变成块大小的整数倍而填充的一种标准方式。
PKCS7 填充规则
- 计算需要补多少个字节(N)使数据长度变成块大小的整数倍。
- 然后在数据末尾添加 N 个字节,每个字节的值都是 N。
例如:假设块大小是 16 字节,原始数据是:
"HELLO WORLD" # 11 字节
差 5 字节才够一个完整块,所以填充:
"HELLO WORLD\x05\x05\x05\x05\x05"
每个填充字节是 \x05,表示填充了 5 个字节。
解密时,根据最后一个字节的值(比如 5),将最后的 5 个字节去掉,还原原始数据。
PKCS7 常用于AES-CBC 模式、AES-ECB 模式、其他基于块的加密算法。
业务中的加密规则
加密方法
- AES(设备xxx|||设备xxxx|||设备xx|||设备xxx),例如:AES(co45zjXXXXXta5ss|||bxxxxxf6da6xxxxxdbc5|||xxxxxX1100xxxxx|||1246684457)
- 加密KEY:设备xxx(前16字节)
- 对齐方法:PKCS7
- 加密模式:CBC
- 初始化向量:xxx
代码实现
安装pycryptodome库
pip install pycryptodome
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64
import timedef aes_crypt(xxx, xxxx, xx, random_num, xxx):plaintext = f"{xxx}|||{xxxx}|||{xx}|||{random_num}"plaintext_bytes = plaintext.encode('utf-8')padded_data = pad(plaintext_bytes, AES.block_size)key = xxx[:16].encode('utf-8')iv = xxxcipher = AES.new(key, AES.MODE_CBC, iv)ciphertext = cipher.encrypt(padded_data)encoded = base64.b64encode(ciphertext).decode('utf-8')return encodedif __name__ == '__main__':xxx = "6xxxxxA7B1E7xxxx"xxxx = "gxxxxx63416S7xxxxx44A9BFE6xxxx"xx = "Cxxxxx12006xxxx"random = str(int(time.time()))xxx = "rApXXXXXfkGg55RXXXXXoL9238XXXXXH"encrypt_data = aes_crypt(xxx, xxxx, xx, random, xxx)print(encrypt_data)print(random)
plaintext_bytes = plaintext.encode('utf-8')
- 将字符串 plaintext 转换成字节数据(bytes 类型)才能进行加密。
- 加密算法只能处理字节(bytes),不能加密字符串。
- 转换后就变成这样:
b'6xxxxxA7B1E7xxxx|||gxxxxx63416S7xxxxx44A9BFE6xxxx|||Cxxxxx12006xxxx|||1652731469'
padded_data = pad(plaintext_bytes, AES.block_size)
- 对字节数据进行 PKCS7 填充(padding),确保数据长度是 AES 所需的16 字节的倍数。
- AES 是分组加密算法,一次只能加密 16 字节。如果不够,需要补齐。
- pad() 是来自 Crypto.Util.Padding 的函数。
- 例如:原始字节长度:73,加密需要的长度:80(下一个 16 的倍数),就会补 7 个字节,每个字节的值是 07(十六进制)
key = xxx[:16].encode('utf-8')
- 将原始密钥(key)取前 16 个字符,然后转为 UTF-8 编码的字节串,作为 AES 加密的密钥。
- AES 加密要求密钥长度必须是 16、24 或 32 字节(分别对应 AES-128、AES-192、AES-256)。
- 如果原来的 key 比较长,这里只取了前 16 个字符。
- encode(‘utf-8’) 是将字符串转为 bytes 类型,因为 AES 库要求输入是 bytes。
iv = xxx
- 设置初始化向量(IV),这里只示意说一下,业务中的初始化向量就不说了,用于 CBC 模式。
- iv 是 CBC 模式下用来打乱加密结果的一个初始向量,长度必须是 16 字节。
cipher = AES.new(key, AES.MODE_CBC, iv)
- 使用 AES 创建一个 加密器对象,选择的加密模式是 CBC 模式。
- AES.new(…) 创建一个 AES 加密器。
- AES.MODE_CBC 是密码块链接模式,适用于加密多块数据,安全性好于 ECB。
ciphertext = cipher.encrypt(padded_data)
- 将原始明文数据(必须是 16 的倍数长度)加密成密文。
- padded_data 是明文数据,已经填充好到 16 字节倍数。
- encrypt() 方法会将其加密,返回 ciphertext(密文),类型为 bytes。