Основная проблема с вашим кодом заключается в том, что вы используете строки.AES работает с двоичными данными, и если вы используете PyCryptodome, этот код вызовет ошибку TypeError:
Object type <class 'str'> cannot be passed to C code
Pycrypto принимает строки, но внутренне кодирует их в байты, поэтому не имеет смысла декодировать ваши байтыв строку, потому что он будет закодирован обратно в байты.Кроме того, он кодирует с помощью ASCII (протестировано с PyCrypto v2.6.1, Python v2.7) и так, например, этот код:
encryptor.encrypt(u'ψ' * 16)
вызовет UnicodeEncodeError:
File "C:\Python27\lib\site-packages\Crypto\Cipher\blockalgo.py", line 244, in encrypt
return self._cipher.encrypt(plaintext)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-15
Вы должны всегда использовать байты при шифровании или дешифровании данных.Затем вы можете декодировать открытый текст в строку, если это текст.
Следующая проблема - ваш метод заполнения.Он генерирует строку, поэтому вы получаете ошибку TypeError, когда пытаетесь применить ее к открытому тексту, который должен быть байтами.Это можно исправить, если вы дополняете байтами
chunk += <b>b' '</b> * (16 - len(chunk) % 16)
, но было бы лучше использовать заполнение PKCS7 (в настоящее время вы используете заполнение нулями, но с пробелом вместо нулевого байта).
PyCryptodome предоставляет функции заполнения, но, похоже, вы используете PyCrypto.В этом случае вы можете реализовать заполнение PKCS7 или, что еще лучше, скопировать функции заполнения PyCryptodome.
try:
from Crypto.Util.Padding import pad, unpad
except ImportError:
from Crypto.Util.py3compat import bchr, bord
def pad(data_to_pad, block_size):
padding_len = block_size-len(data_to_pad)%block_size
padding = bchr(padding_len)*padding_len
return data_to_pad + padding
def unpad(padded_data, block_size):
pdata_len = len(padded_data)
if pdata_len % block_size:
raise ValueError("Input data is not padded")
padding_len = bord(padded_data[-1])
if padding_len<1 or padding_len>min(block_size, pdata_len):
raise ValueError("Padding is incorrect.")
if padded_data[-padding_len:]!=bchr(padding_len)*padding_len:
raise ValueError("PKCS#7 padding is incorrect.")
return padded_data[:-padding_len]
Функции pad
и unpad
были скопированы из Crypto.Util.Padding
и изменены для использования только дополнения PKCS7.Обратите внимание, что при использовании отступов PKCS7 важно заполнить последний фрагмент, даже если его размер кратен размеру блока, иначе вы не сможете правильно удалить блок.
Применение этих изменений к функции encrypt_file
,
def encrypt_file(key, in_filename, out_filename=None, chunksize=64*1024):
if not out_filename:
out_filename = in_filename + '.enc'
iv = os.urandom(16)
encryptor = AES.new(key, AES.MODE_CBC, iv)
filesize = os.path.getsize(in_filename)
with open(in_filename, 'rb') as infile:
with open(out_filename, 'wb') as outfile:
outfile.write(struct.pack('<Q', filesize))
outfile.write(iv)
pos = 0
while pos < filesize:
chunk = infile.read(chunksize)
pos += len(chunk)
if pos == filesize:
chunk = pad(chunk, AES.block_size)
outfile.write(encryptor.encrypt(chunk))
и соответствующей функции decrypt_file
,
def decrypt_file(key, in_filename, out_filename=None, chunksize=64*1024):
if not out_filename:
out_filename = in_filename + '.dec'
with open(in_filename, 'rb') as infile:
filesize = struct.unpack('<Q', infile.read(8))[0]
iv = infile.read(16)
encryptor = AES.new(key, AES.MODE_CBC, iv)
with open(out_filename, 'wb') as outfile:
encrypted_filesize = os.path.getsize(in_filename)
pos = 8 + 16 # the filesize and IV.
while pos < encrypted_filesize:
chunk = infile.read(chunksize)
pos += len(chunk)
chunk = encryptor.decrypt(chunk)
if pos == encrypted_filesize:
chunk = unpad(chunk, AES.block_size)
outfile.write(chunk)
Этот код совместим с Python2 / Python3,и он должен работать либо с PyCryptodome, либо с PyCrypto.
Однако, если вы используете PyCrypto, я рекомендую обновить до PyCryptodome.PyCryptodome является форком PyCrypto и предоставляет тот же API (так что вам не придется слишком сильно изменять код), а также некоторые дополнительные функции: функции заполнения, алгоритмы аутентифицированного шифрования, KDF и т. Д. С другой стороны, PyCrypto не являетсяподдерживаются больше, а также, некоторые версии страдают от уязвимости переполнения буфера кучи: CVE-2013-7459 .