You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python 3.x中处理pgcrypto加密数据?求推荐适用库

从pgcrypto迁移至Python的AES加解密方案

推荐的Python加密库

  • pycryptodome:功能全面的老牌加密库,支持所有AES模式,文档完善,适合快速适配现有逻辑。
  • cryptography:由Python核心团队维护的现代加密库,API设计更安全、直观,内置安全最佳实践,推荐用于新项目。

关键适配点:pgcrypto默认行为解析

pgcrypto的encrypt('hello', 'mypass', 'aes')默认遵循以下规则,Python端必须严格模拟才能正确加解密:

  1. 密钥处理:将输入密钥用\x00填充至AES有效长度(16/24/32字节,对应128/192/256位),取填充后的前N字节作为最终密钥。
  2. IV设置:使用全0的16字节IV(AES块大小为16字节)。
  3. 加密模式:AES-CBC模式。
  4. 填充方式:PKCS#5填充(与PKCS#7兼容,块大小16字节时两者无区别)。
  5. 输出格式:密文的十六进制字符串(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 21:35:54