import base64
import logging
import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from olc.encryptor.encryptor import Encryptor
logger = logging.getLogger(__name__)
class AesEncryptor(Encryptor):
"""
AES 加密器实现,使用 AES-CBC 模式和 PBKDF2 密钥派生。
特点:
- 使用 AES-256-CBC 加密算法
- 通过 PBKDF2HMAC 从密码派生密钥
- 每次加密使用随机 salt 和 IV,确保相同明文加密结果不同
- 加密数据格式: [salt(16B)][IV(16B)][ciphertext][PKCS7填充]
Example:
>>> encryptor = AesEncryptor("my_password")
>>> encrypted = encryptor.encrypt("敏感数据")
>>> decrypted = encryptor.decrypt(encrypted)
>>> decrypted
'敏感数据'
"""
def __init__(self, password: str, salt: bytes = None):
"""
初始化 AES 加密器。
Args:
password: 用于派生加密密钥的密码字符串
salt: 可选的盐值,用于密钥派生。如果不提供,会自动生成随机盐值。
盐值会被包含在加密结果中,解密时会自动提取。
"""
self.password = password.encode('utf-8')
self.salt = salt if salt else os.urandom(16)
self.key = self._derive_key()
def _derive_key(self) -> bytes:
"""
使用 PBKDF2HMAC 从密码派生 AES 密钥。
通过多次迭代的哈希运算,将用户密码转换为安全的加密密钥,
防止暴力破解和彩虹表攻击。
Returns:
bytes: 32字节的 AES-256 密钥
"""
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=self.salt,
iterations=100000,
backend=default_backend()
)
return kdf.derive(self.password)
def encrypt(self, raw: str) -> str:
"""
加密字符串。
Args:
raw: 要加密的明文字符串
Returns:
str: Base64 编码的加密结果,格式为: salt + IV + 密文
Note:
每次加密会生成新的随机 IV,因此相同明文多次加密结果不同。
"""
if not raw:
return raw
iv = os.urandom(16)
cipher = Cipher(algorithms.AES(self.key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
padder = padding.PKCS7(128).padder()
padded_data = padder.update(raw.encode('utf-8')) + padder.finalize()
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
encrypted_data = self.salt + iv + ciphertext
return base64.b64encode(encrypted_data).decode('utf-8')
def decrypt(self, raw: str) -> str:
"""
解密字符串。
Args:
raw: Base64 编码的加密字符串
Returns:
str: 解密后的明文字符串
Raises:
Exception: 解密失败时抛出异常(如密码错误、数据损坏等)
"""
try:
if not raw:
return raw
encrypted_data = base64.b64decode(raw)
salt = encrypted_data[:16]
iv = encrypted_data[16:32]
ciphertext = encrypted_data[32:]
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend()
)
key = kdf.derive(self.password)
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
padded_data = decryptor.update(ciphertext) + decryptor.finalize()
unpadder = padding.PKCS7(128).unpadder()
data = unpadder.update(padded_data) + unpadder.finalize()
return data.decode('utf-8')
except Exception:
logger.error(f"AES decrypt error, value: {raw}")
return raw