Подтвердить что ты не робот

Шифрование Python с помощью PyCrypto AES

Я просто нашел pycrypto сегодня, и я работал над своим классом шифрования AES. К сожалению, он работает только наполовину. self.h.md5 выводит хеш md5 в шестнадцатеричном формате и составляет 32 байт. Это результат. Кажется, что оно расшифровывает сообщение, но после дешифрования оно помещает случайные символы, в этом случае \n\n\n... Я думаю, что у меня проблема с размером блока self.data, кто-нибудь знает, как это исправить?

Jans-MacBook-Pro: test2 jan $../../bin/python3 data.py b'RLfGmn5jf5WTJphnmW0hXG7IaIYcCRpjaTTqwXR6yiJCUytnDib + GQYlFORm + jIctest 1 2 3 4 5 endtest\n\n\n\n\n\n\n\n\n\n '

from Crypto.Cipher import AES
from base64 import b64encode, b64decode
from os import urandom

class Encryption():
    def __init__(self):
        self.h = Hash()

    def values(self, data, key):
        self.data = data
        self.key = key
        self.mode = AES.MODE_CBC
        self.iv = urandom(16)
        if not self.key:
            self.key = Cfg_Encrypt_Key
        self.key = self.h.md5(self.key, True)

    def encrypt(self, data, key):
        self.values(data, key)
        return b64encode(self.iv + AES.new(self.key, self.mode, self.iv).encrypt(self.data))

    def decrypt(self, data, key):
        self.values(data, key)
        self.iv = b64decode(self.data)[:16]
        return AES.new(self.key, self.mode, self.iv).decrypt(b64decode(self.data)[16:])
4b9b3361

Ответ 1

Если честно, символы "\n\n\n\n\n\n\n\n\n\n" не кажутся мне случайными.; -)

Вы используете AES в режиме CBC. Это требует, чтобы длина открытого текста и зашифрованного текста всегда была кратной 16 байтам. С помощью кода, который вы показываете, вы должны увидеть, что возникает исключение, когда data, переданное в encrypt(), не выполняет такое условие. Похоже, вы добавили достаточно новых символов строки ('\n', чтобы вход был до тех пор, пока не был выровнен текст.

Кроме того, существует два распространенных способа решения проблемы выравнивания:

  • Переключитесь с CBC (AES.MODE_CBC) на CFB (AES.MODE_CFB). При использовании по умолчанию segment_size, используемого PyCrypto, у вас не будет никаких ограничений на длину открытого текста и зашифрованного текста.

  • Сохраните CBC и используйте схему заполнения, такую ​​как PKCS # 7, а именно:

    • перед тем, как зашифровать открытый текст из X байтов, добавьте в обратную сторону столько же байтов, сколько необходимо для достижения следующей 16-байтовой границы. Все байты заполнения имеют одинаковое значение: количество добавляемых байтов:

      length = 16 - (len(data) % 16)
      data += bytes([length])*length
      

      Этот стиль Python 3. В Python 2 у вас будет:

      length = 16 - (len(data) % 16)
      data += chr(length)*length
      
    • после дешифрования удалите из задней части открытого текста столько байтов, сколько указано в дополнении:

      data = data[:-data[-1]]
      

Несмотря на то, что в вашем случае я понимаю, что это просто упражнение класса, я хотел бы указать, что отправлять данные без какой-либо проверки подлинности (например, MAC) небезопасно.

Ответ 2

from hashlib import md5
from Crypto.Cipher import AES
from Crypto import Random
import base64

def derive_key_and_iv(password, salt, key_length, iv_length):
    d = d_i = ''
    while len(d) < key_length + iv_length:
        d_i = md5(d_i + password + salt).digest()
        d += d_i
    return d[:key_length], d[key_length:key_length+iv_length]

def encrypt(in_file, out_file, password, key_length=32):
    bs = AES.block_size
    salt = Random.new().read(bs - len('Salted__'))
    key, iv = derive_key_and_iv(password, salt, key_length, bs)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    #print in_file
    in_file = file(in_file, 'rb')
    out_file = file(out_file, 'wb')
    out_file.write('Salted__' + salt)
    finished = False
    while not finished:
        chunk = in_file.read(1024 * bs)
        if len(chunk) == 0 or len(chunk) % bs != 0:
            padding_length = bs - (len(chunk) % bs)
            chunk += padding_length * chr(padding_length)
            finished = True
        out_file.write(cipher.encrypt(chunk))
    in_file.close()
    out_file.close()

def decrypt(in_file, out_file, password, key_length=32):
    bs = AES.block_size

    in_file = file(in_file, 'rb')
    out_file = file(out_file, 'wb')
    salt = in_file.read(bs)[len('Salted__'):]
    key, iv = derive_key_and_iv(password, salt, key_length, bs)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    next_chunk = ''
    finished = False
    while not finished:
        chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
        if len(next_chunk) == 0:
            padding_length = ord(chunk[-1])
            if padding_length < 1 or padding_length > bs:
               raise ValueError("bad decrypt pad (%d)" % padding_length)
            # all the pad-bytes must be the same
            if chunk[-padding_length:] != (padding_length * chr(padding_length)):
               # this is similar to the bad decrypt:evp_enc.c from openssl program
               raise ValueError("bad decrypt")
            chunk = chunk[:-padding_length]
            finished = True
        out_file.write(chunk)    
    in_file.close()
    out_file.close()

def encode(in_file, out_file):
    in_file = file(in_file, 'rb')
    out_file = file(out_file, 'wb')
    data = in_file.read()
    out_file.write(base64.b64encode(data))    
    in_file.close()
    out_file.close()

def decode(in_file, out_file):
    in_file = file(in_file, 'rb')
    out_file = file(out_file, 'wb')
    data = in_file.read()
    out_file.write(base64.b64decode(data))    
    in_file.close()
    out_file.close()

Ответ 3

AES.new().encrypt() и .decrypt() принимают как строки ввода и вывода, длина которых кратная 16. Вы должны так или иначе исправить ее. Например, вы можете сохранить реальную длину в начале и использовать ее для обрезания дешифрованной строки.

Обратите внимание также, что, хотя это единственное ограничение для AES, другие модули (особенно в Crypto.PublicKey) имеют дополнительные ограничения, которые исходят из их математической реализации и которые не должны (по-моему) быть видимыми конечному пользователю, но находятся. Например, Crypto.PublicKey.ElGamal будет шифровать любую короткую строку, но если она начинается с нулевых символов, они теряются при расшифровке.