PHP 7.2 openssl_encrypt и mcrypt_encrypt генерируют разные значения - PullRequest
1 голос
/ 04 октября 2019

Я переношу все функции моего проекта, которые шифруют данные и работают с mcrypt, в openssl.

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

При расшифровке я получаю правильный результат с обеими функциями;проблема в том, что я делюсь этой информацией с внешним провайдером и успешно расшифровываю данные, только если я шифрую с помощью mcrypt.

Это тестовый код:

// Configuration.
$data = 'FOO';
$secret = '111222333444555666777888';
$iv = 'ABCDEFGH';

// Encrytp & decrypt with mcrypt.
$encMcrypt = bin2hex(mcrypt_encrypt(MCRYPT_3DES, $secret, utf8_encode($data),  MCRYPT_MODE_CBC, $iv));
$decMcrypt = mcrypt_decrypt(MCRYPT_3DES, $secret, hex2bin($encMcrypt), MCRYPT_MODE_CBC, $iv);

// Encrytp & decrypt with openssl.
$encOpenSSL = bin2hex(openssl_encrypt(utf8_encode($data), 'DES-EDE3-CBC', $secret, OPENSSL_RAW_DATA, $iv));
$decOpenSSL = openssl_decrypt(hex2bin($encOpenSSL), 'DES-EDE3-CBC', $secret, OPENSSL_RAW_DATA, $iv);

// Result.
echo "mcrypt encrypt: $encMcrypt <br>";
echo "openssl encrypt: $encOpenSSL <br>";
echo "mcrypt decrypt: $decMcrypt <br>";
echo "openssl decrypt: $decOpenSSL";

Результат:

mcrypt encrypt: 3f9bd8d5f844ff67
openssl encrypt: b2f4b9aeb07e1ca4
mcrypt decrypt: FOO
openssl decrypt: FOO

Кто-нибудь знает, почему это происходит потому, что они получают разные результаты?

Спасибо!

Ответы [ 3 ]

2 голосов
/ 04 октября 2019

Разница в том, что mcrypt_encrypt / mcrypt_decrypt использует заполнение нулями и openssl_encrypt / openssl_decrypt использует PKCS7-Padding . Это можно легко проверить, применив Zero-Padding для openssl: для этого PKCS7-Padding должен быть отключен с флагом OPENSSL_ZERO_PADDING ( важно : несмотря на название, этот флаг не означает, чтоЗаполнение нулями используется, но заполнение вообще не применяется), и открытый текст должен дополняться значениями 0 до следующего целого числа, кратного размеру блока (8 байтов для Triple-DES), если длина уже не указанасоответствует целому кратному размеру блока:

<?php

function zeroPadding($data, $size) {
    $oversize = strlen($data) % $size;
    return $oversize == 0 ? $data : ($data . str_repeat("\0", $size - $oversize)); 
}

// Something is wronguration.
$data = 'FOO';

$secret = '111222333444555666777888';
$iv = 'ABCDEFGH';

// Encrytp & decrypt with mcrypt.
$encMcrypt = bin2hex(mcrypt_encrypt(MCRYPT_3DES, $secret, utf8_encode($data), MCRYPT_MODE_CBC, $iv));
$decMcrypt = mcrypt_decrypt(MCRYPT_3DES, $secret, hex2bin($encMcrypt), MCRYPT_MODE_CBC, $iv);

// Encrytp & decrypt with openssl.
$encOpenSSLZeroPadding = bin2hex(openssl_encrypt(utf8_encode(zeroPadding($data, 8)), 'DES-EDE3-CBC', $secret, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv));
$decOpenSSLZeroPadding = openssl_decrypt(hex2bin($encOpenSSLZeroPadding), 'DES-EDE3-CBC', $secret, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);

// Result.
echo "data padded: " . bin2hex(zeroPadding($data, 8)) . "<br>";
echo "mcrypt encrypt: $encMcrypt <br>";
echo "openssl encrypt: $encOpenSSLZeroPadding <br>";
echo "mcrypt decrypt: $decMcrypt <br>";
echo "mcrypt decrypt: " . bin2hex($decMcrypt) . "<br>";
echo "openssl decrypt: $decOpenSSLZeroPadding" . "<br>";
echo "openssl decrypt: " . bin2hex($decOpenSSLZeroPadding);

со следующим выводом:

data padded: 464f4f0000000000
mcrypt encrypt: 3f9bd8d5f844ff67
openssl encrypt: 3f9bd8d5f844ff67
mcrypt decrypt: FOO
mcrypt decrypt: 464f4f0000000000
openssl decrypt: FOO
openssl decrypt: 464f4f0000000000

Вместо использования заполнения нулями в openssl -контексте, PKCS7-Обивка может быть использована в mcrypt -контексте. Независимо от того, какой из двух вариантов используется, с одинаковыми отступами mcrypt и openssl результаты идентичны!

Следует отметить (см. Шестнадцатеричный вывод), что mcrypt не удаляет ноль-Паддинг во время дешифрования (в отличие от openssl, который удаляет PKCS7-Padding во время дешифрования). Кроме того, Zero-Padding ненадежен по сравнению с PKCS7-Padding. Если требования позволяют это (что в вашем случае, вероятно, не так из-за внешнего поставщика), следует использовать PKCS7-Padding.

Кроме того, оба опубликованных варианта используют один и тот же алгоритм, а именно Triple-DES в CBC -режим. Triple-DES имеет размер блока 8 байтов и размер ключа 24 байтов. Triple-DES не идентичен DES , но основан на DES в том смысле, что состоит из трех прогонов DES (encryption-decryption-encryption = ede). mcrypt указывает Triple-DES / CBC с двумя параметрами, MCRYPT_3DES (Triple-DES) и MCRYPT_MODE_CBC (режим CBC), в то время как openssl использует только один параметр, DES-EDE3-CBC.

Для Triple-DES существует несколько Keying-Options . 3TDEA использует три независимых ключа DES и указывается в контексте openssl с DES-EDE3-CBC, что предполагает 24 байтовый ключ, 2TDEA использует два независимых ключа и может быть задано в контексте opensslальтернативно с DES-EDE-CBC, который ожидает ключ 16.

Triple-DES намного медленнее, чем современный AES, но имеет сопоставимую защиту. Как и в случае с отступами, вы должны переключиться на AES, если это возможно.

1 голос
/ 04 октября 2019

Mcrypt и openssl - два разных шифра, поэтому они шифруют и дешифруют данные по-разному.

Вы можете использовать метод desSSE-ede3 openSSL, однако вы действительно должны использовать AES: Сравнение безопасностиAES и DES

Если ваш внешний поставщик использует mcrypt_decrypt для расшифровки отправляемых вами данных, он не сможет расшифровать данные, зашифрованные с помощью openssl, и наоборот.

Вашему внешнему провайдеру также потребуется изменить способ дешифрования данных, если вы перейдете на более безопасное шифрование AES openSSL.

(примечание: хорошо, что вы переходите наopenssl - mcrypt заброшен около php 7.1)

1 голос
/ 04 октября 2019

На мой взгляд, оба метода используют разные алгоритмы, см. этот вопрос . Вы должны попытаться использовать des-ede3 в качестве метода шифрования openssl, который эквивалентен MCRYPT_3DES.

для mcrypt.
...