Java — расшифровка шифрования Python AES

У меня есть зашифрованный текст из Java (v8) с использованием AES, который я пытаюсь расшифровать в python, используя тот же SecretKey, Salt, но у меня возникает проблема при распаковке индекса вне диапазона. Когда я делаю обратное, то есть шифрую в python и расшифровываю в java, я могу получить текст, но с каким-то нежелательным префиксом.

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

Код Java (Base64 из org.apache.commons.codec.binary.Base64)

public static String encrypt(String secretKey, String salt, String value) throws Exception {
        Cipher cipher = initCipher(secretKey, salt, Cipher.ENCRYPT_MODE);
        byte[] encrypted = cipher.doFinal(value.getBytes());
        return Base64.encodeBase64String(encrypted);
    }

    public static String decrypt(String secretKey, String salt, String encrypted) throws Exception {
        Cipher cipher = initCipher(secretKey, salt, Cipher.DECRYPT_MODE);
        byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
        return new String(original);
    }

    private static Cipher initCipher(String secretKey, String salt, int mode) throws Exception {

        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");

        KeySpec spec = new PBEKeySpec(secretKey.toCharArray(), salt.getBytes(), 65536, 256);
        SecretKey tmp = factory.generateSecret(spec);
        SecretKeySpec skeySpec = new SecretKeySpec(tmp.getEncoded(), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(mode, skeySpec, new IvParameterSpec(new byte[16]));
        return cipher;
    }

    public static void main(String[] args) throws Exception {
        String secretKey = "Secret";
        String fSalt = "tJHnN5b1i6wvXMwzYMRk";
        String plainText = "England";

        String cipherText = encrypt(secretKey, fSalt, plainText);
        System.out.println("Cipher: " + cipherText);
//      cipherText = "6peDTxE1xgLE4hTGg0PKTnuuhFC1Vftsd7NH9DF/7WM = "; // Cipher from python
        String dcrCipherText = decrypt(secretKey, fSalt, cipherText);
        System.out.println(dcrCipherText);

    }

Код Python (версия 3.6) и Pycrypto V2.6

import base64
import hashlib
import os

from Crypto.Cipher import AES

BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)

# unpad = lambda s: s[:-ord(s[len(s) - 1:])]
unpad = lambda s: s[0:-s[-1]]

def get_private_key(secretKey, salt):
    key = hashlib.pbkdf2_hmac('SHA256', secretKey.encode(), salt.encode(), 65536, 32)
    return key


def encrypt(message, salt, secretKey):
    private_key = get_private_key(secretKey, salt)
    message = pad(message)
    iv = os.urandom(BS)  # 128-bit IV
    cipher = AES.new(private_key, AES.MODE_CBC, iv, segment_size=256)
    return base64.b64encode(iv + cipher.encrypt(message))


def decrypt(enc, salt, secretKey):
    private_key = get_private_key(secretKey, salt)
    enc = base64.b64decode(enc)
    iv = enc[:BS]
    cipher = AES.new(private_key, AES.MODE_CBC, iv, segment_size=256)
    return unpad(cipher.decrypt(enc[BS:]))


secretKey = "Secret"
salt = "tJHnN5b1i6wvXMwzYMRk"
plainText = "England"
cipher = encrypt(plainText, salt, secretKey)
print("Cipher: " + bytes.decode(cipher))

# cipher = "0JrZdg9YBRshfTdr1d4zwQ= = " # Cipher from java
decrypted = decrypt(cipher, salt, secretKey)
print("Decrypted " + bytes.decode(decrypted))

Вывод Java Decrypt: �U�����or���England когда я передаю шифр Python, ожидается: England Вывод расшифровки Python: unpad = lambda s : s[0:-s[-1]] IndexError: index out of range, ожидаемый: England

Я также просмотрел другой пост в стеке по этой проблеме, но это не сработало, поскольку они использовали другой режим.

Рабочий образец можно найти по адресу здесь.

Another coder 24.07.2020 12:30
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
1
2 601
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В python вы сохраняете iv (вектор инициализации) в первых 16 байтах зашифрованного сообщения.

В Java вы ничего подобного не делаете — вы передаете пустой IV и обрабатываете все сообщение, включая первые 16 байтов, как зашифрованный текст.

Вы должны убедиться, что Java и Python совпадают.

Либо вы не используете IV в обоих случаях, и в этом случае вы удаляете эту часть в Python.

Или вы используете IV в обоих случаях, и в этом случае вам нужно обновить свой код Java, чтобы сгенерировать случайный IV при шифровании, добавив его к результату шифрования. При расшифровке код Java должен взять первые 16 байтов в качестве IV и передать их в Cipher.

Спасибо за ответ, я понимаю, что часть IV добавляется в шифрование python, но когда я удаляю его в python, он генерирует шифр, который метод расшифровки java не может расшифровать и выбросить javax.crypto.BadPaddingException: Given final block not properly padded, а Питон расшифровать также выбросить unpad = lambda s: s[0:-s[-1]] IndexError: index out of range И я попробовал изменения упомянутый выше в java, который работал хорошо, но я не могу изменить логику на стороне java, так как это повлияет на другие функции. Итак, можете ли вы указать, что именно мне нужно изменить в функции расшифровки Python?

Another coder 13.06.2019 10:43

В Java вы предоставляете 16 байтов с нулевым значением в качестве IV (new byte[16] создает массив из 16 байтов, все они инициализированы нулем). Таким образом, вам нужно будет сделать то же самое в python вместо iv = os.urandom(BS) или iv = enc[:BS] - и вам больше не следует хранить IV в закодированном сообщении.

Erwin Bolwidt 13.06.2019 10:46

Я инициализировал iv = "\x00"*BS и не добавляю iv в шифр, который генерирует тот же шифр, что и java, но python не может его расшифровать, получая unpad = lambda s: s[0:-s[-1]] IndexError: index out of range. Я удалил iv = enc[:BS] в функции расшифровки. Можете ли вы помочь мне, что еще мне нужно сделать, чтобы он мог правильно расшифровать. Спасибо еще раз!

Another coder 13.06.2019 12:23

Хорошо, наконец, удалось решить, изменив return unpad(cipher.decrypt(enc[BS:])) на return unpad(cipher.decrypt(enc)) в функции расшифровки Python.

Another coder 13.06.2019 12:32

Правильный код python 3 с использованием pycryptodome, который будет соответствовать коду Java, будет таким:

import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad,pad
import hashlib

def get_private_key(secretKey, salt):
    # _prf = lambda p,s: HMAC.new(p, s, SHA256).digest()
    # private_key = PBKDF2(secretKey, salt.encode(), dkLen=32,count=65536, prf=_prf )
    # above code is equivalent but slow
    key = hashlib.pbkdf2_hmac('SHA256', secretKey.encode(), salt.encode(), 65536, 32)
    # KeySpec spec = new PBEKeySpec(secretKey.toCharArray(), salt.getBytes(), 65536, 256);
    return key

def encrypt(message, salt, secretKey):
    private_key = get_private_key(secretKey, salt)
    message = pad(message.encode(), AES.block_size)
    iv = "\x00"*AES.block_size  # 128-bit IV
    cipher = AES.new(private_key, AES.MODE_CBC, iv.encode())
    return base64.b64encode(cipher.encrypt(message))


def decrypt(enc, salt, secretKey):
    # _prf = lambda p,s: HMAC.new(p, s, SHA256).digest()
    # private_key = PBKDF2(secretKey, salt.encode(), dkLen=32,count=65536, prf=_prf )
    private_key = get_private_key(secretKey, salt)
    enc = base64.b64decode(enc)
    iv = "\x00"*AES.block_size
    cipher = AES.new(private_key, AES.MODE_CBC, iv.encode())
    return unpad(cipher.decrypt(enc), AES.block_size).decode('utf-8')


secretKey = "Secret"
salt = "tJHnN5b1i6wvXMwzYMRk"
plainText = "England"
enc_datta = encrypt(plainText, salt, secretKey)
print(f"Encrypted: {enc_datta}")
# Encrypted: 0JrZdg9YBRshfTdr1d4zwQ==

cipher = "0JrZdg9YBRshfTdr1d4zwQ= = " # Cipher from java
decrypted = decrypt(cipher, salt, secretKey)
print(f"Decrypted: {decrypted}" )
# Decrypted: England

Другие вопросы по теме