AES CBC模式批量文件夹解密报错:数据长度非块长度倍数
批量AES加密文件夹后解密报错:数据长度不是块长度的倍数
为提升业务安全性,我用Python编写了AES加密脚本批量加密文件夹,加密过程正常,但执行自动生成的decryptor.py时触发ValueError,提示「提供的数据长度不是块长度的倍数」。以下是相关代码、报错信息及可用的单个文件加密解密代码,寻求技术解决。
加密代码
import os from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import padding backend = default_backend() key = os.urandom(32) # 256 bit iv = os.urandom(16) # 128 bit # cryptografy cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend) encryptor = cipher.encryptor() # loop all files and folders for root, dirs, files in os.walk('.'): # ignores main.py (that is the file name to not encrypt it) if 'main.py' in files: continue # crypt files for file in files: # create a new encryptor cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend) encryptor = cipher.encryptor() # read datas file_path = os.path.join(root, file) data = open(file_path, 'rb').read() # adding padding padder = padding.PKCS7(128).padder() padded_data = padder.update(data) + padder.finalize() # encrypts the data cipher_data = encryptor.update(padded_data) + encryptor.finalize() # writes the encrypted data to the file file = open(file_path, 'wb') file.write(cipher_data) file.close() # write the code for decryption in the file decryptor_file = open('decryptor.py', 'w') decryptor_file.write("import os\n") decryptor_file.write("from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes\n") decryptor_file.write("from cryptography.hazmat.backends import default_backend\n") decryptor_file.write("from cryptography.hazmat.primitives import padding\n\n") decryptor_file.write("backend = default_backend()\n") decryptor_file.write("key = \"" + key.hex() + "\"\n") decryptor_file.write("iv = \"" + iv.hex() + "\"\n\n") decryptor_file.write("# descryptografy\n") decryptor_file.write("cipher = Cipher(algorithms.AES(bytes.fromhex(key)), modes.CBC(bytes.fromhex(iv)), backend=backend)\n") decryptor_file.write("decryptor = cipher.decryptor()\n\n") decryptor_file.write("# loops all files and folders\n") decryptor_file.write("for root, dirs, files in os.walk('.'):\n") decryptor_file.write(" # decrypt files\n") decryptor_file.write(" for file in files:\n") decryptor_file.write(" # data reading\n") decryptor_file.write(" file_path = os.path.join(root, file)\n") decryptor_file.write(" data = open(file_path, 'rb').read()\n\n") decryptor_file.write(" # decrypt data \n") decryptor_file.write(" decrypted_data = decryptor.update(data) + decryptor.finalize()\n\n") decryptor_file.write(" # remove padding\n") decryptor_file.write(" unpadder = padding.PKCS7(128).unpadder()\n") decryptor_file.write(" unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()\n\n") decryptor_file.write(" # writes the data descrypted in the file\n") decryptor_file.write(" file = open(file_path, 'wb')\n") decryptor_file.write(" file.write(unpadded_data)\n") decryptor_file.write(" file.close()\n\n") decryptor_file.close() # shows a alert message print('All files and folders have been encrypted and the decrypt file has been created!')
自动生成的decryptor.py核心逻辑
上述加密脚本生成的decryptor.py实际执行代码如下:
import os from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import padding backend = default_backend() key = "生成的key hex值" iv = "生成的iv hex值" # descryptografy cipher = Cipher(algorithms.AES(bytes.fromhex(key)), modes.CBC(bytes.fromhex(iv)), backend=backend) decryptor = cipher.decryptor() # loops all files and folders for root, dirs, files in os.walk('.'): # decrypt files for file in files: # data reading file_path = os.path.join(root, file) data = open(file_path, 'rb').read() # decrypt data decrypted_data = decryptor.update(data) + decryptor.finalize() # remove padding unpadder = padding.PKCS7(128).unpadder() unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize() # writes the data descrypted in the file file = open(file_path, 'wb') file.write(unpadded_data) file.close()
报错信息
Traceback (most recent call last): File "C:\Users\User\Desktop\cryptor\decryptor.py", line 23, in <module> decrypted_data = decryptor.update(data) + decryptor.finalize() File "C:\Program Files\Python38\lib\site-packages\cryptography\hazmat\primitives\ciphers\base.py", line 186, in finalize data = self._ctx.finalize() File "C:\Program Files\Python38\lib\site-packages\cryptography\hazmat\backends\openssl\ciphers.py", line 223, in finalize raise ValueError( ValueError: The length of the provided data is not a multiple of the block length.
单个文件加密解密可用代码
import os from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import padding backend = default_backend() key = os.urandom(32) # 256 bit iv = os.urandom(16) # 128 bit # cryptography cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend) encryptor = cipher.encryptor() # read from file file = open('text.txt', 'rb') data = file.read() file.close() # adding the padding padder = padding.PKCS7(128).padder() padded_data = padder.update(data) + padder.finalize() # encrypt the data cipher_data = encryptor.update(padded_data) + encryptor.finalize() # write the encrypted data to the file file = open('crypto.txt', 'wb') file.write(cipher_data) file.close() # delete the old file os.remove('text.txt') # display alert message print('Your file has been encrypted!') # create the decryption file decryptor_file = open('decryptor.py', 'w') # write the decryption code to the file decryptor_file.write("import os\n") decryptor_file.write("from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes\n") decryptor_file.write("from cryptography.hazmat.backends import default_backend\n") decryptor_file.write("from cryptography.hazmat.primitives import padding\n\n") decryptor_file.write("backend = default_backend()\n") decryptor_file.write("key = \"" + key.hex() + "\"\n") decryptor_file.write("iv = \"" + iv.hex() + "\"\n\n") decryptor_file.write("# decryption\n") decryptor_file.write("cipher = Cipher(algorithms.AES(bytes.fromhex(key)), modes.CBC(bytes.fromhex(iv)), backend=backend)\n") decryptor_file.write("decryptor = cipher.decryptor()\n\n") decryptor_file.write("# read file\n") decryptor_file.write("file = open('crypto.txt', 'rb')\n") decryptor_file.write("data = file.read()\n") decryptor_file.write("file.close()\n\n") decryptor_file.write("# decrypt data\n") decryptor_file.write("decrypted_data = decryptor.update(data) + decryptor.finalize()\n\n") decryptor_file.write("# remove padding\n") decryptor_file.write("unpadder = padding.PKCS7(128).unpadder()\n") decryptor_file.write("unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()\n\n") decryptor_file.write("# write decrypted data to file\n") decryptor_file.write("file = open('text.txt', 'wb')\n") decryptor_file.write("file.write(unpadded_data)\n") decryptor_file.write("file.close()\n\n") decryptor_file.close()
问题根源与修复方案
问题根源
解密脚本中复用了同一个decryptor实例处理所有文件。CBC模式的解密器在调用finalize()后,内部状态会被销毁,无法再次用于解密操作。后续文件使用已失效的解密器时,会导致数据处理逻辑异常,触发块长度不匹配的报错。
对比单个文件的代码,单个文件仅执行一次解密流程,所以不会出现这个问题;但批量处理时,每个文件都需要独立的解密器实例。
另外,加密脚本中已经做到了为每个文件创建新的encryptor,但解密脚本未同步这个逻辑。
修复后的decryptor.py代码
将解密器的创建逻辑移到文件循环内部,确保每个文件都使用全新的解密器:
import os from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import padding backend = default_backend() key = "加密脚本生成的key hex值" iv = "加密脚本生成的iv hex值" # loops all files and folders for root, dirs, files in os.walk('.'): # 跳过解密脚本自身,避免误处理 if 'decryptor.py' in files: continue # decrypt files for file in files: # 为每个文件创建新的解密器 cipher = Cipher(algorithms.AES(bytes.fromhex(key)), modes.CBC(bytes.fromhex(iv)), backend=backend) decryptor = cipher.decryptor() # data reading file_path = os.path.join(root, file) with open(file_path, 'rb') as f: data = f.read() # decrypt data decrypted_data = decryptor.update(data) + decryptor.finalize() # remove padding unpadder = padding.PKCS7(128).unpadder() unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize() # writes the data decrypted in the file with open(file_path, 'wb') as f: f.write(unpadded_data)
加密脚本的额外优化
- 修复目录文件跳过逻辑:原代码中
if 'main.py' in files: continue会跳过整个目录的文件处理,应改为在文件循环中判断跳过单个文件:
# loop all files and folders for root, dirs, files in os.walk('.'): # crypt files for file in files: # 跳过加密脚本自身 if file == 'main.py': continue # 后续加密逻辑不变...
- 使用
with语句管理文件操作,避免忘记关闭文件导致的资源泄漏。
内容的提问来源于stack exchange,提问作者BloodHound
相关产品推荐
相关产品推荐

