Python PyCrypto Szyfruj / odszyfruj pliki tekstowe za pomocą AES

Mam już działający program, ale jedyną rzeczą, która nie działa, jest funkcja decrypt_file(), którą mam. Nadal mogę skopiować zaszyfrowany tekst z pliku i umieścić go w mojej funkcji decrypt() i sprawić, by działał, ale kiedy próbuję użyć mojej rzekomo poręcznej funkcji decrypt_file(), pojawia się błąd. Teraz wiem na 99.999%, że moje funkcje encrypt() i decrypt() są w porządku, ale jest coś z konwersją bajtów i łańcuchów, gdy czytam i koduję plik tekstowy, który wyrzuca błąd; I nie mogę znaleźć przerwy. Proszę o pomoc!

Mój Program:

from Crypto import Random
from Crypto.Cipher import AES

def encrypt(message, key=None, key_size=256):
    def pad(s):
        x = AES.block_size - len(s) % AES.block_size
        return s + ((bytes([x])) * x)

    padded_message = pad(message)

    if key is None:
        key = Random.new().read(key_size // 8)

    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key, AES.MODE_CBC, iv)

    return iv + cipher.encrypt(padded_message)

def decrypt(ciphertext, key):
    unpad = lambda s: s[:-s[-1]]
    iv = ciphertext[:AES.block_size]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    plaintext = unpad(cipher.decrypt(ciphertext))[AES.block_size:]

    return plaintext

def encrypt_file(file_name, key):
    f = open(file_name, 'r')
    plaintext = f.read()
    plaintext = plaintext.encode('utf-8')
    enc = encrypt(plaintext, key)
    f.close()
    f = open(file_name, 'w')
    f.write(str(enc))
    f.close()

def decrypt_file(file_name, key):
    def pad(s):
        x = AES.block_size - len(s) % AES.block_size
        return s + ((str(bytes([x]))) * x)

    f = open(file_name, 'r')
    plaintext = f.read()
    x = AES.block_size - len(plaintext) % AES.block_size
    plaintext += ((bytes([x]))) * x
    dec = decrypt(plaintext, key)
    f.close()
    f = open(file_name, 'w')
    f.write(str(dec))
    f.close()



key = b'\xbf\xc0\x85)\x10nc\x94\x02)j\xdf\xcb\xc4\x94\x9d(\x9e[EX\xc8\xd5\xbfI{\xa2$\x05(\xd5\x18'

encrypt_file('to_enc.txt', key)

Plik tekstowy, który zaszyfrowałem:

b';c\xb0\xe6Wv5!\xa3\xdd\xf0\xb1\xfd2\x90B\x10\xdf\x00\x82\x83\x9d\xbc2\x91\xa7i M\x13\xdc\xa7'

Mój błąd podczas próby decrypt_file:

    Traceback (most recent call last):
  File "C:\Python33\testing\test\crypto.py", line 56, in <module>
    decrypt_file('to_enc.txt', key)
  File "C:\Python33\testing\test\crypto.py", line 45, in decrypt_file
    plaintext += ((bytes([x]))) * x
TypeError: Can't convert 'bytes' object to str implicitly
[Finished in 1.5s]

Kiedy zamieniam linię 45 na: plaintext += ((str(bytes([x])))) * x, to jest błąd, który dostaję:

Traceback (most recent call last):
  File "C:\Python33\testing\test\crypto.py", line 56, in <module>
    decrypt_file('to_enc.txt', key)
  File "C:\Python33\testing\test\crypto.py", line 46, in decrypt_file
    dec = decrypt(plaintext, key)
  File "C:\Python33\testing\test\crypto.py", line 23, in decrypt
    plaintext = unpad(cipher.decrypt(ciphertext))[AES.block_size:]
  File "C:\Python33\lib\site-packages\Crypto\Cipher\blockalgo.py", line 295, in decrypt
    return self._cipher.decrypt(ciphertext)
ValueError: Input strings must be a multiple of 16 in length
[Finished in 1.4s with exit code 1]
Author: Trooper Z, 2013-12-31

2 answers

Przyjrzałem się bliżej twojemu kodowi i zobaczyłem, że jest z nim kilka problemów. Pierwszym z nich jest to, że krypto działa z bajtami, a nie tekstem. Więc lepiej jest po prostu zachować dane jako ciąg bajtów. Odbywa się to po prostu poprzez umieszczenie znaku " b " w trybie. W ten sposób możesz pozbyć się wszystkich kodowań i konwersji bajtów, które próbowałeś zrobić.

Przepisałem cały kod również przy użyciu nowszych idiomów Pythona. Tutaj jest.

#!/usr/bin/python3

from Crypto import Random
from Crypto.Cipher import AES

def pad(s):
    return s + b"\0" * (AES.block_size - len(s) % AES.block_size)

def encrypt(message, key, key_size=256):
    message = pad(message)
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    return iv + cipher.encrypt(message)

def decrypt(ciphertext, key):
    iv = ciphertext[:AES.block_size]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    plaintext = cipher.decrypt(ciphertext[AES.block_size:])
    return plaintext.rstrip(b"\0")

def encrypt_file(file_name, key):
    with open(file_name, 'rb') as fo:
        plaintext = fo.read()
    enc = encrypt(plaintext, key)
    with open(file_name + ".enc", 'wb') as fo:
        fo.write(enc)

def decrypt_file(file_name, key):
    with open(file_name, 'rb') as fo:
        ciphertext = fo.read()
    dec = decrypt(ciphertext, key)
    with open(file_name[:-4], 'wb') as fo:
        fo.write(dec)


key = b'\xbf\xc0\x85)\x10nc\x94\x02)j\xdf\xcb\xc4\x94\x9d(\x9e[EX\xc8\xd5\xbfI{\xa2$\x05(\xd5\x18'

encrypt_file('to_enc.txt', key)
#decrypt_file('to_enc.txt.enc', key)
 30
Author: Keith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-01 11:43:09

W Pythonie 3 (którego wyraźnie używasz) domyślnym trybem dla otwieranych plików jest tekst, a nie binarny. Gdy czytasz z pliku, otrzymujesz ciągi znaków, a nie tablice bajtowe. To nie pasuje do szyfrowania.

W kodzie należy zastąpić:

open(file_name, 'r')

Z:

open(file_name, 'rb')

To samo, gdy otwierasz plik do zapisu. W tym momencie możesz pozbyć się wszystkich różnych wystąpień, w których konwertujesz z ciągu znaków na binarne i odwrotnie.

Na przykład, to może odejść:

plaintext = plaintext.encode('utf-8')
 3
Author: SquareRootOfTwentyThree,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-31 14:21:52