如何在Python 3.x中处理pgcrypto加密数据?求推荐适用库
从pgcrypto迁移至Python的AES加解密方案
推荐的Python加密库
- pycryptodome:功能全面的老牌加密库,支持所有AES模式,文档完善,适合快速适配现有逻辑。
- cryptography:由Python核心团队维护的现代加密库,API设计更安全、直观,内置安全最佳实践,推荐用于新项目。
关键适配点:pgcrypto默认行为解析
pgcrypto的encrypt('hello', 'mypass', 'aes')默认遵循以下规则,Python端必须严格模拟才能正确加解密:
- 密钥处理:将输入密钥用
\x00填充至AES有效长度(16/24/32字节,对应128/192/256位),取填充后的前N字节作为最终密钥。 - IV设置:使用全0的16字节IV(AES块大小为16字节)。
- 加密模式:AES-CBC模式。
- 填充方式:PKCS#5填充(与PKCS#7兼容,块大小16字节时两者无区别)。
- 输出格式:密文的十六进制字符串(PostgreSQL中
\x前缀是十六进制标识,实际密文为对应字节序列)。
代码实现示例
使用pycryptodome
pip install pycryptodome
from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad def pgcrypto_aes_encrypt(plaintext, key): # 处理密钥:用\x00填充至AES有效长度 key_bytes = key.encode('utf-8') if len(key_bytes) < 16: key_bytes = key_bytes.ljust(16, b'\x00') elif len(key_bytes) < 24: key_bytes = key_bytes.ljust(24, b'\x00') else: key_bytes = key_bytes.ljust(32, b'\x00') # 截取对应长度的密钥 if len(key_bytes) >= 32: key_final = key_bytes[:32] elif len(key_bytes) >=24: key_final = key_bytes[:24] else: key_final = key_bytes[:16] # 初始化CBC加密器,IV为全0 iv = b'\x00' * 16 cipher = AES.new(key_final, AES.MODE_CBC, iv) # 填充明文并加密 padded_plain = pad(plaintext.encode('utf-8'), AES.block_size) ciphertext = cipher.encrypt(padded_plain) return ciphertext.hex() def pgcrypto_aes_decrypt(ciphertext_hex, key): key_bytes = key.encode('utf-8') if len(key_bytes) < 16: key_bytes = key_bytes.ljust(16, b'\x00') elif len(key_bytes) < 24: key_bytes = key_bytes.ljust(24, b'\x00') else: key_bytes = key_bytes.ljust(32, b'\x00') if len(key_bytes) >= 32: key_final = key_bytes[:32] elif len(key_bytes) >=24: key_final = key_bytes[:24] else: key_final = key_bytes[:16] iv = b'\x00' * 16 cipher = AES.new(key_final, AES.MODE_CBC, iv) # 解密并去除填充 ciphertext = bytes.fromhex(ciphertext_hex) padded_plain = cipher.decrypt(ciphertext) plaintext = unpad(padded_plain, AES.block_size) return plaintext.decode('utf-8') # 测试用户示例 print(pgcrypto_aes_encrypt('hello', 'mypass')) # 输出: f690d690cd27c34523dcc95e8d16112a print(pgcrypto_aes_decrypt('f690d690cd27c34523dcc95e8d16112a', 'mypass')) # 输出: hello
使用cryptography
pip install cryptography
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives import padding from cryptography.hazmat.backends import default_backend def pgcrypto_aes_encrypt(plaintext, key): key_bytes = key.encode('utf-8') # 处理密钥 if len(key_bytes) < 16: key_bytes = key_bytes.ljust(16, b'\x00') elif len(key_bytes) < 24: key_bytes = key_bytes.ljust(24, b'\x00') else: key_bytes = key_bytes.ljust(32, b'\x00') if len(key_bytes) >= 32: key_final = key_bytes[:32] elif len(key_bytes) >=24: key_final = key_bytes[:24] else: key_final = key_bytes[:16] # 初始化CBC加密器 iv = b'\x00' * 16 cipher = Cipher(algorithms.AES(key_final), modes.CBC(iv), backend=default_backend()) encryptor = cipher.encryptor() # PKCS#7填充 padder = padding.PKCS7(128).padder() padded_plain = padder.update(plaintext.encode('utf-8')) + padder.finalize() ciphertext = encryptor.update(padded_plain) + encryptor.finalize() return ciphertext.hex() def pgcrypto_aes_decrypt(ciphertext_hex, key): key_bytes = key.encode('utf-8') # 处理密钥 if len(key_bytes) < 16: key_bytes = key_bytes.ljust(16, b'\x00') elif len(key_bytes) < 24: key_bytes = key_bytes.ljust(24, b'\x00') else: key_bytes = key_bytes.ljust(32, b'\x00') if len(key_bytes) >= 32: key_final = key_bytes[:32] elif len(key_bytes) >=24: key_final = key_bytes[:24] else: key_final = key_bytes[:16] iv = b'\x00' * 16 cipher = Cipher(algorithms.AES(key_final), modes.CBC(iv), backend=default_backend()) decryptor = cipher.decryptor() # 解密并去填充 ciphertext = bytes.fromhex(ciphertext_hex) padded_plain = decryptor.update(ciphertext) + decryptor.finalize() unpadder = padding.PKCS7(128).unpadder() plaintext = unpadder.update(padded_plain) + unpadder.finalize() return plaintext.decode('utf-8') # 测试用户示例 print(pgcrypto_aes_encrypt('hello', 'mypass')) # 输出: f690d690cd27c34523dcc95e8d16112a print(pgcrypto_aes_decrypt('f690d690cd27c34523dcc95e8d16112a', 'mypass')) # 输出: hello
关于IV的常见疑问
不是所有AES库都适用,你需要选择支持CBC模式且允许自定义IV的库(上述两个库均满足)。pgcrypto未显式设置IV时,默认使用全0的16字节IV,只要在Python端模拟这一行为,无需额外IV参数即可完成加解密。
注意:使用全0IV存在安全风险(相同明文会生成相同密文),如果是新开发的加密逻辑,建议改用随机IV并将IV与密文一同存储;但迁移现有数据时必须严格遵循pgcrypto的默认规则。
内容的提问来源于stack exchange,提问作者mithun_daa
相关产品推荐
相关产品推荐

