使用Python和Pycryptodome实现输入字符串加解密失败问题排查
解决Pycryptodome中CBC模式的"Data must be padded to 16 byte boundary"错误
你遇到的ValueError: Data must be padded to 16 byte boundary in CBC mode错误,核心原因有三个:
- CBC模式要求加密和解密使用相同的初始化向量(IV),原代码每次创建AES对象时自动生成新IV,导致解密时无法还原正确数据,进而触发填充校验失败。
- 密文是二进制数据,直接通过
input()获取后用encode()转换会破坏原始二进制结构,导致解密时数据长度或内容不符合要求。 - 代码中
salt被注释,PBKDF2需要随机salt生成稳定密钥,缺失salt会导致密钥生成逻辑异常。
修正方案
- 统一IV的使用:加密时生成随机IV,将IV与密文绑定(比如拼接后转Base64),解密时拆分出IV再使用。
- 二进制数据用Base64编码:把二进制的IV、密文转成Base64字符串,避免输入输出时的数据损坏。
- 补全随机salt生成:使用
Crypto.Random.get_random_bytes生成随机salt,且加密时需将salt与IV、密文一同保存,确保解密时能生成相同密钥。
修正后的完整代码
from Crypto.Protocol.KDF import PBKDF2 from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from Crypto.Random import get_random_bytes import base64 class SingleEncryption: def __init__(self, password, salt=None): self.password = password.encode() # 统一转字节 # 若未传入salt则生成随机salt(长度推荐16字节) self.salt = salt if salt else get_random_bytes(16) # PBKDF2迭代次数建议设为10000+,增强密钥安全性 self.key = PBKDF2(self.password, self.salt, dkLen=32, count=100000) def saveKey(self): choice = input(f"是否保存密钥及salt到文件?Y/N ") if choice.upper() == "Y": file_path = input("请输入保存路径:") # 用with语句自动管理文件,避免资源泄漏 with open(file_path, "wb") as file_out: # 先存salt再存密钥,方便后续读取 file_out.write(self.salt + self.key) print(f"密钥及salt已保存到 {file_path}") else: print(f"未保存,salt: {base64.b64encode(self.salt).decode()}, 密钥: {base64.b64encode(self.key).decode()}") def encrypt(self, data): # 生成随机IV(AES块大小为16字节,IV长度需与块大小一致) iv = get_random_bytes(AES.block_size) cipher = AES.new(self.key, AES.MODE_CBC, iv) ciphered_data = cipher.encrypt(pad(data, AES.block_size)) # 将salt、IV、密文拼接后转Base64,方便传输/存储 return base64.b64encode(self.salt + iv + ciphered_data).decode() def decrypt(self, encrypted_str): # 解码Base64字符串为二进制 encrypted_data = base64.b64decode(encrypted_str) # 拆分salt(前16字节)、IV(中间16字节)、密文(剩余部分) salt = encrypted_data[:16] iv = encrypted_data[16:32] cipher_data = encrypted_data[32:] # 用拆分出的salt重新生成密钥(确保和加密时一致) key = PBKDF2(self.password, salt, dkLen=32, count=100000) cipher2 = AES.new(key, AES.MODE_CBC, iv) original_data = unpad(cipher2.decrypt(cipher_data), AES.block_size) return original_data.decode() if __name__ == "__main__": password = input("请输入加密密码:") encrypt = SingleEncryption(password) while True: q = input("选择操作:Encrypt(加密)/ Decrypt(解密)/ Quit(退出)") if q == "Encrypt": data = input("请输入要加密的内容:").encode() print("加密结果:", encrypt.encrypt(data)) elif q == "Decrypt": encrypted_str = input("请输入加密后的字符串:") try: print("解密结果:", encrypt.decrypt(encrypted_str)) except ValueError as e: print("解密失败:", str(e)) elif q == "Quit": break else: print("输入错误,请区分大小写,重新输入。")
关键说明
- IV的作用:CBC模式下IV用于保证相同明文加密后得到不同密文,必须随机且与密文一同传递,解密时必须使用同一个IV。
- Base64编码:二进制数据无法直接通过文本输入框正确传输,转成Base64字符串后可安全存储和输入。
- PBKDF2参数:增加
count参数(迭代次数)可以提升密钥破解难度,推荐设置100000次以上。
内容的提问来源于stack exchange,提问作者FireFlower
相关产品推荐
相关产品推荐

