web-dev-qa-db-fra.com

Python PyCrypto crypter / décrypter des fichiers texte avec AES

J'ai déjà un programme de travail, mais la seule chose qui ne fonctionne pas est la fonction decrypt_file() que j'ai. Je peux toujours copier le texte crypté du fichier et le mettre dans ma fonction decrypt() et le faire fonctionner, mais quand j'essaie d'utiliser ma fonction supposée pratique decrypt_file() renvoie une erreur. Maintenant, je sais à 99,999% que mes fonctions encrypt() et decrypt() sont correctes, mais il y a quelque chose avec la conversion d'octets et de chaînes lorsque je lis et encode le fichier texte qui génère une erreur; Je ne trouve tout simplement pas le raccrochage. Aidez-moi!

Mon programme:

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)

Le fichier texte que j'ai chiffré:

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

Mon erreur lors de la tentative de 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]

Lorsque je remplace la ligne 45 par: plaintext += ((str(bytes([x])))) * x, voici l'erreur que j'obtiens:

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]
11
Zach King

J'ai regardé de plus près votre code et j'ai vu qu'il y avait plusieurs problèmes avec celui-ci. La première est que la crypto fonctionne avec des octets, pas du texte. Il est donc préférable de simplement conserver les données sous forme de chaîne d'octets. Cela se fait simplement en mettant un caractère "b" dans le mode. De cette façon, vous pouvez vous débarrasser de tout le codage et de la conversion d'octets que vous tentiez de faire.

J'ai réécrit tout le code en utilisant également des idiomes plus récents Python. Le voici.

#!/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)
29
Keith

Dans Python 3 (que vous utilisez clairement), le mode par défaut pour les fichiers que vous ouvrez est le texte, pas binaire. Lorsque vous lisez le fichier, vous obtenez des chaînes plutôt que des tableaux d'octets. Cela ne signifie pas aller de pair avec le cryptage.

Dans votre code, vous devez remplacer:

open(file_name, 'r')

avec:

open(file_name, 'rb')

Il en va de même lorsque vous ouvrez le fichier en écriture. À ce stade, vous pouvez vous débarrasser de toutes les différentes occurrences où vous convertissez de chaîne en binaire et vice versa.

Par exemple, cela peut disparaître:

plaintext = plaintext.encode('utf-8')
3