PostgreSQL加密数据后Python解密无输出问题求助
解决PostgreSQL AES加密后Python解密失败的问题
我帮你梳理下这个问题,大概率是加密和解密的逻辑没对齐,甚至加密函数的设计本身有坑,咱们一步步排查解决:
先搞清楚你的PostgreSQL加密函数逻辑
你给出的函数片段里,IV是用明文数据 + 'salt'做哈希生成的——这其实是个错误设计!IV不应该依赖明文,否则解密时必须知道明文才能生成IV,这完全矛盾,而且也不符合加密安全规范。不过先不管这个,先把加密函数的完整逻辑补全,才能对应写Python解密代码。
假设你的完整加密函数是这样的(大概率和实际情况一致):
CREATE OR REPLACE FUNCTION AESEncrypt(data TEXT, pass TEXT) RETURNS TEXT AS $crypted$ declare crypted BYTEA; key BYTEA; iv BYTEA; BEGIN -- 用SHA256生成32字节的AES-256密钥 key := digest(convert_to(pass, 'utf-8'), 'sha256'); -- 用明文+salt做MD5哈希,取前16字节当IV(这就是坑的来源) iv := substring(digest(convert_to(CONCAT(data , 'salt'), 'utf-8'), 'md5') from 1 for 16); -- 用AES-CBC模式+PKCS7填充加密 crypted := encrypt(convert_to(data, 'utf-8'), key, iv, 'aes-cbc/pkcs7'); -- 把加密结果转成Base64字符串返回 RETURN encode(crypted, 'base64'); END; $crypted$ LANGUAGE plpgsql;
为什么你的Python解密失败?
核心问题就是这个IV的生成逻辑——解密时你需要原始明文才能生成和加密时一样的IV,但你解密的目的就是得到明文啊!这就陷入死循环了。另外还有可能是以下细节没对齐:
- 加密结果的编码(是Base64还是十六进制?)
- AES的模式、填充方式和PG不一致
- 密钥的哈希算法和PG不一样
解决方案:先修正加密逻辑(推荐)
首先得把加密函数改成安全合理的写法——用随机生成的IV,把IV和密文一起存储,这样解密时就能从密文中提取IV了:
修正后的PostgreSQL加密函数
CREATE OR REPLACE FUNCTION AESEncrypt(data TEXT, pass TEXT) RETURNS TEXT AS $crypted$ declare crypted BYTEA; key BYTEA; iv BYTEA; BEGIN key := digest(convert_to(pass, 'utf-8'), 'sha256'); -- 生成随机16字节IV(符合AES-CBC要求) iv := gen_random_bytes(16); crypted := encrypt(convert_to(data, 'utf-8'), key, iv, 'aes-cbc/pkcs7'); -- 把IV拼在密文前面,一起转成Base64返回 RETURN encode(iv || crypted, 'base64'); END; $crypted$ LANGUAGE plpgsql;
对应的Python解密代码
先安装依赖库:pip install pycryptodome pandas(pandas用来处理CSV)
import base64 from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import hashlib import pandas as pd def aes_decrypt(crypted_str, password): # 1. 解码Base64字符串 crypted_data = base64.b64decode(crypted_str) # 2. 拆分IV(前16字节)和密文(剩下的部分) iv = crypted_data[:16] ciphertext = crypted_data[16:] # 3. 生成和PG完全一致的密钥:SHA256哈希密码 key = hashlib.sha256(password.encode('utf-8')).digest() # 4. 初始化AES-CBC解密器 cipher = AES.new(key, AES.MODE_CBC, iv) # 5. 解密并去除PKCS7填充 decrypted_data = unpad(cipher.decrypt(ciphertext), AES.block_size) # 6. 转成UTF-8字符串返回 return decrypted_data.decode('utf-8') # 处理CSV文件:解密指定列 if __name__ == "__main__": # 读取加密后的CSV df = pd.read_csv('encrypted_data.csv') # 替换成你要解密的列名和密码 df['decrypted_column'] = df['encrypted_column'].apply(lambda x: aes_decrypt(x, '你的加密密码')) # 保存解密后的CSV df.to_csv('decrypted_data.csv', index=False)
如果必须兼容现有加密数据(不推荐)
如果你已经有大量加密数据不能重新加密,那只能硬着头皮按照原来的错误逻辑写解密代码——但前提是你得知道加密前的原始明文(这显然不合理,但没办法):
import base64 from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import hashlib import pandas as pd def aes_decrypt_old(crypted_str, password, original_plaintext): # 1. 解码Base64 ciphertext = base64.b64decode(crypted_str) # 2. 生成和PG一致的IV:明文+salt做MD5哈希取前16字节 iv = hashlib.md5(f"{original_plaintext}salt".encode('utf-8')).digest()[:16] # 3. 生成密钥 key = hashlib.sha256(password.encode('utf-8')).digest() # 4. 解密 cipher = AES.new(key, AES.MODE_CBC, iv) decrypted_data = unpad(cipher.decrypt(ciphertext), AES.block_size) return decrypted_data.decode('utf-8')
你看,这个函数必须传入原始明文才能解密,完全失去了解密的意义,所以强烈建议你改用随机IV的加密逻辑。
内容的提问来源于stack exchange,提问作者Emil Rowland
相关产品推荐
相关产品推荐

