Расшифровка AES с использованием Pycrypto Python Exception: 'builtins.UnicodeDecodeError: кодек utf-8 не может декодировать байт 0x80 в позиции 0: недопустимый начальный байт'

Я использую следующую реализацию шифрования AES: -

import hashlib
from Crypto.Cipher import AES

class AESCipher:
    def __init__(self, key):
        self.BS = 128
        try:
            self.key = hashlib.sha256(key.encode()).digest()[:self.BS]
        except:
            self.key = hashlib.sha256(key).digest()[:self.BS]
        self.iv = Random.new().read(AES.block_size)
    def encrypt(self, raw):
        raw = self._pad(raw)
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        return base64.b64encode(self.iv + cipher.encrypt(raw))
    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        self.iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode()
    def _pad(self, s):
        return s + (self.BS - len(s) % self.BS) * chr(self.BS - len(s) % self.BS).encode()
    @staticmethod
    def _unpad(s):
        return s[:-ord(s[len(s)-1:])]

Шифрование объекта словаря с двоичной кодировкой не вызывает ошибок, но когда я пытаюсь расшифровать тот же зашифрованный объект, возникает следующее исключение: -

return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode()
builtins.UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte

Я пытался использовать функции кодирования и декодирования «ISO» и «latin». Но после этого сокет на другой стороне локальной сети распознает его как строку, а не как объект словаря.

Мой вопрос: - Что я делаю неправильно здесь?

Дополнительная информация :-

key = 'SHSJDS-DSJBSJDS-DSKNDS' # some thing following this pattern

bin_json_object = pickle.dumps(dict_object)
enc_json_object = AESenc(bin_json_object, key)

def AESenc(self, data, key):
    return AESCipher(key).encrypt(data)

def AESdec(self, data, key):
    return AESCipher(key).decrypt(data)

Например, если я использую кодировку "ISO-8859-1" в приведенном выше коде: -

двоичное кодированное представление объекта словаря: -

b'\x80\x03}q\x00(X\x02\x00\x00\x00idq\x01X$\x00\x00\x0096e09f6c-1e80-4cd1-9225-159e35bcacb4q\x02X\x0c\x00\x00\x00request_codeq\x03K\x01X\x0e\x00\x00\x00payload_lengthq\x04K!X\x0b\x00\x00\x00session_keyq\x05Nu.'

зашифрованное представление объекта словаря в двоичной кодировке: -

 b'cZi+L4Wi51B5oDGQKlFb9bioxKH3TFRO1piECklafwTe6GYm/VeVjJaCDKiI+o6f6CcUnMvx+2EfEwcHCH/KDDeHTivIUou7WGVrd1P++HxfYNutY/aOn30Y/yiICvwWRHBn/3zU3xXvr/4XrtoVddM2cQEgXupIcC99TIxurrr8CCZd74ZnWj6QB8quCtHD'

Но если я сейчас попытаюсь расшифровать то же самое на другом узле в той же локальной сети через сокет. Я получаю следующее расшифрованное представление: -

}q(XidqX$96e09f6c-1e80-4cd1-9225-159e35bcacb4qX
                                                              request_codeqKXpayload_lengthqK!X
                  session_keyqNu.

что полностью отличается от исходного двоичного представления того же объекта словаря. И выдает следующее исключение: -

data = pickle.loads(data)
builtins.TypeError: 'str' does not support the buffer interface

person Akash Rana    schedule 11.02.2016    source источник
comment
Не могли бы вы предоставить код, который вы используете для его тестирования?   -  person FBidu    schedule 12.02.2016
comment
@FBidu Добавлен необходимый код..   -  person Akash Rana    schedule 12.02.2016
comment
Что вы имеете в виду, вы пробовали «ISO» и «латиницу», должно быть decode("ISO-8859-1")..   -  person l'L'l    schedule 12.02.2016
comment
@l'L'l Я использовал encode('ISO-8859-1') и decode('ISO-8859-1') при выполнении одних и тех же функций..   -  person Akash Rana    schedule 12.02.2016
comment
Расшифрованное представление такое же, как и двоично-кодированное, просто вы не видите символов.   -  person l'L'l    schedule 12.02.2016
comment
self.BS = 128, безусловно, неправильно. Просто используйте self.BS = AES.block_size или self.BS = 16, потому что вы перепутали биты и байты. Правильно ли он расшифровывает, если вы попробуете его без отправки через сокет?   -  person Artjom B.    schedule 12.02.2016
comment
@ArtjomB. Если я расшифрую его, даже не отправляя в сокет, он создаст вышеупомянутое исключение.   -  person Akash Rana    schedule 12.02.2016


Ответы (1)


Наконец, после нескольких часов отладки я получил рабочий код, но не могу понять, почему он работает. Пожалуйста, если кто-то может объяснить это в комментариях. Код шифра модифицированной версии AES: -

class AESCipher:
    def __init__(self, key):
        self.BS = AES.block_size
        try:
            self.key = hashlib.sha256(key.encode('ISO-8859-1')).digest()[:self.BS]
        except:
            self.key = hashlib.sha256(key).digest()[:self.BS]
        self.iv = Random.new().read(AES.block_size)
    def encrypt(self, raw):
        raw = self._pad(raw)
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        return base64.b64encode(self.iv + cipher.encrypt(raw))
    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        self.iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('ISO-8859-1')
    def _pad(self, s):
        return s + (self.BS - len(s) % self.BS) * chr(self.BS - len(s) % self.BS).encode('ISO-8859-1')
    @staticmethod
    def _unpad(s):
        print('returning : ', s[:-ord(s[len(s)-1:])])
        return s[:-ord(s[len(s)-1:])]

Теперь без изменения функций шифрования и дешифрования AES. Я представил следующий вариант кода. Всякий раз, когда другой узел получает двоичный поток, он сначала расшифровывает его с помощью функции расшифровки AES. Но после расшифровки закодированный объект словаря необходимо снова закодировать с помощью «ISO-8859-1», как показано ниже:

dict_object = self.AESdecryption(binary_stream, self.session_key)
dict = pickle.loads(dict_object.encode('ISO-8859-1'))
print(dict)

Приведенное выше создает правильный объект словаря. Но что я не понимаю, так это то, что когда объект словаря был зашифрован в кодировке «ISO-8859-1», а затем расшифрован на другом узле в кодировке «ISO-8859-1», то почему перед передачей его в рассол. load() мне нужно снова закодировать его, чтобы получить исходный объект словаря. Пожалуйста, если кто-нибудь может объяснить, почему это происходит?

person Akash Rana    schedule 12.02.2016
comment
Это скорее вопрос строкового представления, представляющего собой двоичные символы; если бы вы закодировали их как «шестнадцатеричные», возможно, вы могли бы сэкономить несколько шагов. Независимо от этого, Python обычно может только определить, имеет ли строка тип str или unicode... кроме того, вам нужно явно указать это в противном случае. - person l'L'l; 12.02.2016