Django中使用AES-CTR加密文件遇<Object type <class 'tuple'>>错误求助
Django中AES-CTR加密报错问题解决及替代方案
问题概述
加密txt文件时触发错误:
Object type <class 'tuple'> cannot be passed to C code
问题集中在这段代码:
aes = AES.new(key.encode('utf8'), AES.MODE_CTR, counter=ctr)
尝试给ctr加encode会报错:'dict' object has no attribute 'encode';去掉key和ctr的encode则报错:Object type <class 'str'> cannot be passed to C code。
当前在Django中使用AES-128的CTR模式加密,完整代码及调用逻辑如下:
现有代码
加密函数
# AES supports multiple key sizes: 16 (AES128), 24 (AES192), or 32 (AES256). key_bytes = 16 # Takes as input a 32-byte key and an arbitrary-length plaintext and returns a # pair (iv, ciphtertext). "iv" stands for initialization vector. def encrypt(key, testpass): assert len(key) == key_bytes print(testpass) print(key) # Choose a random, 16-byte IV. iv = Random.new().read(AES.block_size) # Convert the IV to a Python integer. iv_int = int(binascii.hexlify(iv), 16) # Create a new Counter object with IV = iv_int. ctr = Counter.new(AES.block_size * 8, initial_value=iv_int) print(ctr) # Create AES-CTR cipher. aes = AES.new(key.encode('utf8'), AES.MODE_CTR, counter=ctr) # Encrypt and return IV and ciphertext. ciphertext = aes.encrypt(testpass) print(iv) print(ciphertext) return (iv, ciphertext)
调用代码
testpass = Audio_store.objects.all().values_list('password').last() enkripsi = encrypt("testingtesting11", testpass)
关键信息
打印testpass显示为*('testpass_3kEMV2T.txt',)*,调用testpass.encode("utf-8")无输出。
解决方案
核心问题定位
传入加密函数的testpass是元组而非字符串:values_list('password').last()返回单元素元组,必须先提取其中的字符串值。
步骤1:修正输入参数
修改调用代码,提取元组中的字符串:
testpass_tuple = Audio_store.objects.all().values_list('password').last() if testpass_tuple: testpass = testpass_tuple[0] # 提取元组内的字符串 enkripsi = encrypt("testingtesting11", testpass) else: # 处理无数据的边界情况 pass
更简洁的写法:使用flat=True直接返回字符串
testpass = Audio_store.objects.all().values_list('password', flat=True).last() if testpass: enkripsi = encrypt("testingtesting11", testpass)
步骤2:调整加密函数的参数处理
aes.encrypt()要求传入字节类型数据,需将明文字符串编码为字节:
# 替换原加密行 ciphertext = aes.encrypt(testpass.encode('utf-8'))
修正后的完整加密函数
import binascii from Crypto.Cipher import AES from Crypto.Random import Random from Crypto.Util import Counter key_bytes = 16 def encrypt(key, testpass): assert len(key) == key_bytes, "密钥长度必须为16字节(AES-128)" # 统一转换为字节类型 key_data = key.encode('utf-8') plaintext_data = testpass.encode('utf-8') # 生成随机IV并初始化计数器 iv = Random.new().read(AES.block_size) iv_int = int(binascii.hexlify(iv), 16) ctr = Counter.new(AES.block_size * 8, initial_value=iv_int) # 创建密码器并加密 aes = AES.new(key_data, AES.MODE_CTR, counter=ctr) ciphertext = aes.encrypt(plaintext_data) return (iv, ciphertext)
替代方案:简化版AES加密实现
利用PyCryptodome的内置语法简化CTR模式实现,更适合Django项目:
import base64 from Crypto.Cipher import AES from Crypto.Random import get_random_bytes def aes_encrypt(key, plaintext): # AES-128要求密钥16字节 assert len(key) == 16, "密钥长度需为16字节" key = key.encode('utf-8') # 生成随机nonce(CTR模式用nonce+计数器替代传统IV) nonce = get_random_bytes(8) cipher = AES.new(key, AES.MODE_CTR, nonce=nonce) ciphertext = cipher.encrypt(plaintext.encode('utf-8')) # 返回base64编码的nonce+密文,方便存储和传输 return base64.b64encode(nonce + ciphertext).decode('utf-8') def aes_decrypt(key, encrypted_data): assert len(key) == 16, "密钥长度需为16字节" key = key.encode('utf-8') # 解码并拆分nonce和密文 data = base64.b64decode(encrypted_data) nonce = data[:8] ciphertext = data[8:] cipher = AES.new(key, AES.MODE_CTR, nonce=nonce) plaintext = cipher.decrypt(ciphertext).decode('utf-8') return plaintext # Django调用示例 testpass = Audio_store.objects.all().values_list('password', flat=True).last() if testpass: encrypted_str = aes_encrypt("testingtesting11", testpass) # 解密示例 decrypted_str = aes_decrypt("testingtesting11", encrypted_str)
内容的提问来源于stack exchange,提问作者HelpMePlease
相关产品推荐
相关产品推荐

