在js逆向遇到AES,CBC模式加密并没有带iv而是每次随机生成的iv,python带的库没有相关实现,不能实现解密,故记录一下python相关实现:
import os
from Crypto.Cipher import AES
import base64
import hashlib
def encrypt_aes(plaintext, password):
# 生成随机盐
salt = os.urandom(8)
# 根据密码和盐生成密钥和 IV
key_iv = b""
last_hash = b""
while len(key_iv) < 48: # 32 字节密钥 + 16 字节 IV
last_hash = hashlib.md5(last_hash + password.encode() + salt).digest()
key_iv += last_hash
key = key_iv[:32] # 前 32 字节是密钥
iv = key_iv[32:48] # 后 16 字节是 IV
# 初始化 AES 加密器
cipher = AES.new(key, AES.MODE_CBC, iv)
# PKCS7 填充
padding_len = 16 - len(plaintext) % 16
padded_plaintext = plaintext + chr(padding_len) * padding_len
# 加密数据
encrypted_data = cipher.encrypt(padded_plaintext.encode())
# 输出格式为 Salted__ + 盐 + 密文
ciphertext = b"Salted__" + salt + encrypted_data
# 使用 Base64 编码返回
return base64.b64encode(ciphertext).decode('utf-8')
def decrypt_aes(ciphertext_base64, password):
# 解码 Base64 密文
ciphertext = base64.b64decode(ciphertext_base64)
# 检查是否以 "Salted__" 开头
if not ciphertext.startswith(b"Salted__"):
raise ValueError("Invalid ciphertext format")
# 提取盐 (salt)
salt = ciphertext[8:16]
# 提取密文部分
encrypted_data = ciphertext[16:]
# 根据密码和盐生成密钥和 IV (Key 和 IV derivation)
key_iv = b""
last_hash = b""
while len(key_iv) < 48: # 32 字节密钥 + 16 字节 IV
last_hash = hashlib.md5(last_hash + password.encode() + salt).digest()
key_iv += last_hash
key = key_iv[:32] # 前 32 字节是密钥
iv = key_iv[32:48] # 后 16 字节是 IV
# 初始化 AES 解密器
cipher = AES.new(key, AES.MODE_CBC, iv)
# 解密数据
decrypted_data = cipher.decrypt(encrypted_data)
# 去除 PKCS7 填充
padding_len = decrypted_data[-1]
if padding_len > 16: # 填充长度不可能超过块大小
raise ValueError("Invalid padding")
decrypted_data = decrypted_data[:-padding_len]
return decrypted_data.decode('utf-8')
if __name__ == '__main__':
# 测试
password = "dRgUkXp2r5u8x/A?D(G+KbPeShVmYq3t"
encrypted = encrypt_aes("{1:2}", password)
dencrypted = decrypt_aes(encrypted, password)
print(encrypted)
print(dencrypted)