Невозможно обменять данные, зашифрованные с помощью AES-256 между Java и PHP

Моя проблема: что я шифрую в Java, я могу отлично расшифровать Java, но PHP mcrypt не может расшифровать. Что я шифрую с помощью mcrypt Я могу расшифровать с помощью mcrypt, но не могу в Java.

Я хочу отправить и получить зашифрованные данные из приложения Java на страницу PHP, поэтому мне нужно, чтобы она была совместимой.

Вот что у меня есть...

JAVA...

public static String crypt(String input, String key){
    byte[] crypted = null;
    try{
        SecretKeySpec skey = new SecretKeySpec(Base64.decodeBase64(key), "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skey);
        crypted = cipher.doFinal(input.getBytes());
    }catch(Exception e){
    }
    return Base64.encodeBase64String(crypted);
}

public static String decrypt(String input, String key){
    byte[] output = null;
    try{
        SecretKeySpec skey = new SecretKeySpec(Base64.decodeBase64(key), "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, skey);
        output = cipher.doFinal(Base64.decodeBase64(input));
    }catch(Exception e){
    }
    return new String(output);
}

Продолжительность:

public static void main(String[] args) {
    String key = "Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb+NONX/LIi8=";
    String data = "example";
    System.out.println(Cpt.decrypt(Cpt.crypt(data, key), key));
}

Вывод:

example

PHP...

function getEncrypt($sStr, $sKey) {
    return base64_encode(
        mcrypt_encrypt(
            MCRYPT_RIJNDAEL_256, 
            $sKey,
            $sStr,
            MCRYPT_MODE_ECB
        )
    );
}

function getDecrypt($sStr, $sKey) {
    return mcrypt_decrypt(
        MCRYPT_RIJNDAEL_256, 
        $sKey, 
        base64_decode($sStr), 
        MCRYPT_MODE_ECB
    );
}

Продолжительность:

$crypt = getDecrypt(getEncrypt($str, $key), $key);
echo "<p>Crypt: $crypt</p>";

Вывод:

Crypt: example�������������������������

Использование PHP для склеивания "пример" с ключом "Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb + NONX/LIi8 =" Я получаю "YTYhgp4zC + w5IsViTR5PUkHMX4i7JzvA6NJT1FqhoGY =". Используя Java для шифрования одной и той же вещи с одним и тем же ключом, я получаю "+ tdAZqTE7WAVPXhB3Tp5 + g ==".

Я кодирую и дешифрую base64 в правильном порядке, и я протестировал base64 кодировать и декодировать совместимость между Java и PHP и работать.

Ответ 1

BUG # 1

MCRYPT_RIJNDAEL_256 не AES. 256 в этой константе ссылаются на блокировку, а не на ключи. Используйте MCRYPT_RIJNDAEL_128, чтобы получить тот же алгоритм, что и AES. Ключ определяется только количеством байтов в указанном ключевом аргументе. Итак, поставьте 32 байта, и вы получите AES с 256-битным ключом.

BUG # 2

Эти две строки никогда не верны в Java и указывают на фундаментальное непонимание природы произвольных двоичных данных, создаваемых криптографическими преобразованиями:

output = cipher.doFinal(Base64.decodeBase64(input));
return new String(output);

Нет ничего плохого в передаче и хранении byte[] напрямую, но если вы должны использовать только печатные строки, тогда вы должны кодировать/декодировать base64, чтобы сделать это. Поскольку вы уже используете base64 экстенсивно, что будет похоже на путь. Я бы предположил, что правильные две строки:

output = cipher.doFinal(Base64.decodeBase64(input));
return new String(Base64.encodeBase64(output), "UTF-8");

ИЗМЕНИТЬ:

Просто шучу о ошибке №2. На самом деле, я был неправ, я не заметил, что это было дешифрование. Конечно, если вы знаете, что дешифрованная byte[] является допустимой строкой, то совершенно правильно делать то, что делает ваш код.

Ответ 2

Я знаю, что это старая тема, но я добавлю свое рабочее решение.

Вы должны переписать сторону PHP из script:

function getEncrypt($sStr, $sKey) {
  return base64_encode(
    mcrypt_encrypt(
        MCRYPT_RIJNDAEL_128, 
        base64_decode($sKey),
        $sStr,
        MCRYPT_MODE_ECB
    )
  );
}

function getDecrypt($sStr, $sKey) {
  return mcrypt_decrypt(
    MCRYPT_RIJNDAEL_128, 
    base64_decode($sKey), 
    base64_decode($sStr), 
    MCRYPT_MODE_ECB
  );
}

Вы должны base64_decode ($ sKey), потому что ваш ключ закодирован в base64.

$key = "Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb+NONX/LIi8=";

Затем вам нужно создать эту функцию (кредит передается лентречи от http://www.php.net/manual/en/function.mcrypt-decrypt.php):

function pkcs5_pad ($text, $blocksize) { 
  $pad = $blocksize - (strlen($text) % $blocksize); 
  return $text . str_repeat(chr($pad), $pad); 
}

Используйте этот код для кодирования/декодирования:

$decrypt = getDecrypt("6XremNEs1jv/Nnf/fRlQob6oG1jkge+5Ut3PL489oIo=", $key);
echo $decrypt;
echo "\n\n";
echo getEncrypt(pkcs5_pad("My very secret text:)", 16), $key);

Я надеюсь, что это будет полезно для кого-то!:)

Ответ 3

Пожалуйста, смотрите здесь:

Проблема, с которой вы сталкиваетесь, является проблемой заполнения. Я не знаю Java, но AES/ECB/PKCS5Padding выглядит так, как будто вы используете дополнение PKCS # 5 (по сути, такое же, как PKCS # 7), в то время как PHP изначально поддерживает NULL -партинг. Что делает PKCS # 5/7:

Поместите ввод с помощью строки заполнения между 1 и 8 байтами, чтобы сделать общая длина, кратная 8 байт. Значение каждого байта строка заполнения задается числом байтов - т.е. 8 байтов значения 0x08, 7 байтов значения 0x07,..., 2 байты 0x02 или один байт значения 0x01.

Таким образом, PHP-код для правильного заполнения является тривиальным:

$blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$padding   = $blockSize - (strlen($data) % $blockSize);
$data      .= str_repeat(chr($padding), $padding);

Ответ 4

Имейте в виду, что для строк используется одинаковая кодировка. Попробуйте преобразовать строки на обоих языках в UTF-8, например, и преобразовать в двоичные данные, которые закодированы:

PHP (s. функция utf8_encode()):

$strAndBlob = utf8_encode("My string");

Java:

String str = "My string";
byte[] blob = str.getBytes("utf-8");

PHP, например, не должен использовать UTF-8 по умолчанию.