CryptoJS и Pycrypto работают вместе

Я шифрую строку в веб-приложении с помощью CryptoJS (v 2.3), и мне нужно расшифровать ее на сервере в Python, поэтому я использую PyCrypto. Я чувствую, что мне чего-то не хватает, потому что я не могу работать.

Вот JS:

Crypto.AES.encrypt('1234567890123456', '1234567890123456',
                   {mode: new Crypto.mode.CBC(Crypto.pad.ZeroPadding)})
// output: "wRbCMWcWbDTmgXKCjQ3Pd//aRasZ4mQr57DgTfIvRYE="

питон:

from Crypto.Cipher import AES
import base64
decryptor = AES.new('1234567890123456', AES.MODE_CBC)
decryptor.decrypt(base64.b64decode("wRbCMWcWbDTmgXKCjQ3Pd//aRasZ4mQr57DgTfIvRYE="))
# output: '\xd0\xc2\x1ew\xbb\xf1\xf2\x9a\xb9\xb6\xdc\x15l\xe7\xf3\xfa\xed\xe4\xf5j\x826\xde(m\xdf\xdc_\x9e\xd3\xb1'

person ian    schedule 19.07.2012    source источник
comment
Поправьте меня, если я ошибаюсь, но я не вижу никаких дополнений в вашем коде Python.   -  person Anonymous    schedule 19.07.2012


Ответы (2)


Вот версия с CryptoJS 3.1.2. Всегда остерегайтесь следующих вещей (используйте одно и то же на обоих языках):

  • Режим работы (в данном случае CBC)
  • Заполнение (в данном случае нулевое заполнение; лучше использовать заполнение PKCS#7)
  • Ключ (та же функция вывода или ключ очистки)
  • Кодировка (одинаковая кодировка для ключа, открытого текста, зашифрованного текста и т. д.)
  • IV (генерируется при шифровании, передается на расшифровку)

Если строка передается в качестве аргумента key функции CryptoJS encrypt(), эта строка используется для получения фактического ключа, который будет использоваться для шифрования. Если вы хотите использовать ключ (допустимые размеры: 16, 24 и 32 байта), вам нужно передать его как WordArray.

Результатом шифрования CryptoJS является строка зашифрованного текста в формате OpenSSL. Чтобы получить из него фактический зашифрованный текст, вам нужно получить доступ к его свойству ciphertext.

IV должен быть случайным для каждого шифрования, чтобы он был семантически безопасным. Таким образом, взглянув только на зашифрованный текст, злоумышленники не могут сказать, является ли один и тот же открытый текст, который был зашифрован несколько раз, на самом деле одним и тем же открытым текстом.

Ниже приведен пример, который я сделал.

JavaScript:

var key = CryptoJS.enc.Utf8.parse('1234567890123456'); // TODO change to something with more entropy

function encrypt(msgString, key) {
    // msgString is expected to be Utf8 encoded
    var iv = CryptoJS.lib.WordArray.random(16);
    var encrypted = CryptoJS.AES.encrypt(msgString, key, {
        iv: iv
    });
    return iv.concat(encrypted.ciphertext).toString(CryptoJS.enc.Base64);
}

function decrypt(ciphertextStr, key) {
    var ciphertext = CryptoJS.enc.Base64.parse(ciphertextStr);

    // split IV and ciphertext
    var iv = ciphertext.clone();
    iv.sigBytes = 16;
    iv.clamp();
    ciphertext.words.splice(0, 4); // delete 4 words = 16 bytes
    ciphertext.sigBytes -= 16;
    
    // decryption
    var decrypted = CryptoJS.AES.decrypt({ciphertext: ciphertext}, key, {
        iv: iv
    });
    return decrypted.toString(CryptoJS.enc.Utf8);
}

Код Python 2 с pycrypto:

BLOCK_SIZE = 16
key = b"1234567890123456" # TODO change to something with more entropy

def pad(data):
    length = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
    return data + chr(length)*length

def unpad(data):
    return data[:-ord(data[-1])]

def encrypt(message, key):
    IV = Random.new().read(BLOCK_SIZE)
    aes = AES.new(key, AES.MODE_CBC, IV)
    return base64.b64encode(IV + aes.encrypt(pad(message)))

def decrypt(encrypted, key):
    encrypted = base64.b64decode(encrypted)
    IV = encrypted[:BLOCK_SIZE]
    aes = AES.new(key, AES.MODE_CBC, IV)
    return unpad(aes.decrypt(encrypted[BLOCK_SIZE:]))

Предупреждение. Имейте в виду, что и python2, и pycrypto устарели, поэтому код необходимо скорректировать, чтобы он соответствовал python3 и pycryptodome.


Другие соображения:

Похоже, вы хотите использовать парольную фразу в качестве ключа. Парольные фразы обычно читаются человеком, а ключи — нет. Вы можете получить ключ из фразы-пароля с помощью таких функций, как PBKDF2, bcrypt или scrypt.

Приведенный выше код не является полностью безопасным, поскольку в нем отсутствует аутентификация. Зашифрованные тексты, не прошедшие проверку подлинности, могут привести к жизнеспособным атакам и незамеченным манипуляциям с данными. Обычно схема «зашифровать-затем-MAC» используется с хорошей функцией MAC, такой как HMAC-SHA256.

person Artjom B.    schedule 23.06.2015
comment
Я не могу передать IV и парольную фразу в AES.new в python, так как для этого требуются байты вместо строки. - person Sanjay Sharma; 19.11.2019
comment
У меня есть аналогичная проблема здесь stackoverflow.com/questions/65844902/ Но у меня есть некоторые дополнительные настройки в CryptoJS CryptoJS.algo.AES.keySize = 32 CryptoJS.algo.EvpKDF.cfg.iterations = 1e4 CryptoJS.algo.EvpKDF.cfg.keySize = 32; И я не знаю, как их реализовать с помощью python - person GhostKU; 25.01.2021
comment
Если вы столкнулись с TypeError с вышеуказанным решением, таким как TypeError: Object type <class 'str'> cannot be passed to C code или TypeError: ord() expected string of length 1, but int found , вы можете обновить свой код следующим образом: * В функции шифрования: return base64.b64encode(IV + aes.encrypt(pad(message).encode())).decode() * В функции дешифрования: return unpad(aes.decrypt(encrypted[BLOCK_SIZE:]).decode()) Спасибо @Artjom B. ! ваше решение спасло мой день. - person Rukamakama; 12.07.2021
comment
@Rukamakama Да, мой ответ довольно старый и нацелен на Python 2 с PyCrypto. Оба не устарели, поэтому код не работает, но его легко адаптировать к Python 3 и pycryptodome. - person Artjom B.; 19.07.2021

Примечание: соль , iv , отступы должны быть одинаковыми в js и python

сгенерировать значение соли и iv и преобразовать его в строку байтов, используя CryptoJS.enc.Utf8.parse()

js-файл

var encrypted = CryptoJS.AES.encrypt(JSON.stringify(json_data), CryptoJS.enc.Utf8.parse(data['salt']) , { iv: CryptoJS.enc.Utf8.parse(data['iv']) , mode: CryptoJS.mode.CBC , padding: CryptoJS.pad.Pkcs7});
en_data = encrypted.ciphertext.toString(CryptoJS.enc.Base64)

отправить эти зашифрованные данные в файл python

файл питона

from Crypto.Util.Padding import pad, unpad

ct = request.POST['encrypted_data']
data = base64.b64decode(ct)
cipher1 = AES.new(salt, AES.MODE_CBC, iv)
pt = unpad(cipher2.decrypt(data), 16)
data = json.loads(pt.decode('utf-8'))
        

pad и upad в pycrypto по умолчанию используют pkcs#7

соль и значение iv должны быть в байтовой строке

person Afsan khan    schedule 25.07.2020
comment
имя 'salt' не определено - person Max; 02.05.2021