Перенос mcrypt с Blowfish и ECB в OpenSSL - PullRequest
0 голосов
/ 27 сентября 2018

Не могу понять, как перенести мой старый код mcrypt в OpenSSL.У меня это работает для Blowfish с CBC и для Rijndael с CBC, но Blowfish с ECB ускользает от меня.

И да, я читаю Переход от mcrypt с Blowfish & ECB к OpenSSL , и япробовал заполнение нулями данных, а не заполнение нулями данных, заполнение нулями ключа, циклическое переключение по клавише и любой их комбинации, и, похоже, ничего не работает.

Это мой код:

<?php
function encrypt_with_mcrypt($data, $key) {
        return mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB);
}

function encrypt_with_openssl($data, $key) {
        return openssl_encrypt($data, 'BF-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY);
}

$data = 'foobar';
$key = 'supersecretkey';

var_dump(base64_encode(encrypt_with_mcrypt($data, $key)));
var_dump(base64_encode(encrypt_with_openssl($data, $key)));

И это вывод:

test.php:13:
string(12) "5z0q3xNnokw="
test.php:14:
string(12) "1zyqavq7sCk="

Ответы [ 2 ]

0 голосов
/ 27 сентября 2018

Мне нечего добавить к ответу Мартена , за исключением того, что я подумал, что было бы неплохо показать код, иллюстрирующий его слова.

mcrypt добавляет нули для заполнениядо открытого текста до кратного размера блока BF, равного 8 байтам, что можно показать, печатая шестнадцатеричные числа как открытого текста, так и зашифрованного зашифрованного текста:

$key = "supersecretkey";
$data = "foobar";
$ctxt = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB);
$ptxt = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $ctxt, MCRYPT_MODE_ECB);
echo bin2hex($data).PHP_EOL;
echo bin2hex($ptxt).PHP_EOL;

дает следующие шестнадцатеричные числа:

666f6f626172
666f6f6261720000

openssl по умолчанию использует заполнение PKCS # 5, которое в данном случае добавляет 2 байта со значением 2 в конце блока:

$key = "supersecretkey";
$data = "foobar";
$opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY;
$ctxt = openssl_encrypt($data, 'BF-ECB', $key, $opts);
$ptxt = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $ctxt, MCRYPT_MODE_ECB);
echo bin2hex($data).PHP_EOL;
echo bin2hex($ptxt).PHP_EOL;

дает

666f6f626172
666f6f6261720202

Зашифрованный текст для mcrypt и openssl может быть согласован путем добавления байтов заполнения вручную.Обратите внимание на опции OPENSSL_ZERO_PADDING и добавление "\0\0":

$key = "supersecretkey";
$data = "foobar";
$ctxt_mc = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB);
$opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY | OPENSSL_ZERO_PADDING;
$ctxt_os = openssl_encrypt($data."\0\0", 'BF-ECB', $key, $opts);
echo bin2hex($ctxt_mc).PHP_EOL;
echo bin2hex($ctxt_os).PHP_EOL;

дает:

e73d2adf1367a24c
e73d2adf1367a24c

В качестве альтернативы, вручную добавляя байты заполнения PKCS # 5 в конце при использовании mcrypt:

$key = "supersecretkey";
$data = "foobar";
$ctxt_mc = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data."\2\2", MCRYPT_MODE_ECB);
$opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY;
$ctxt_os = openssl_encrypt($data, 'BF-ECB', $key, $opts);
echo bin2hex($ctxt_mc).PHP_EOL;
echo bin2hex($ctxt_os).PHP_EOL;

дает

d73caa6afabbb029
d73caa6afabbb029

Наконец, попытка вызвать openssl_encrypt() с отключенным заполнением и длиной, не кратной размеру блока:

$key = "supersecretkey";
$data = "foobar";
$opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY | OPENSSL_ZERO_PADDING;
$ctxt = openssl_encrypt($data, 'BF-ECB', $key, $opts);
echo(openssl_error_string().PHP_EOL)

дает

error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:data not multiple of block length

Примечание: имя OPENSSL_ZERO_PADDING сбивает с толку, но это означает "без заполнения".Вы можете испытать желание использовать флаг OPENSSL_NO_PADDING, но этот флаг не предназначен для использования с openssl_encrypt().Его значение равно 3 , что равно OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING.Вместо этого предназначен для использования с асимметричной криптографией .

0 голосов
/ 27 сентября 2018

В библиотеке / оболочке mcrypt по умолчанию используется заполнение нулем байтов (только при необходимости), в то время как в библиотеке / оболочке OpenSSL по умолчанию используется заполнение PKCS # 5.Это означает, что отдельный блок дополняется по-разному и поэтому будет показывать другой блок зашифрованного текста.


Обычная уловка - расшифровать полученный зашифрованный текст без любой распаковки, а затем проверитьзаполнение байтов путем просмотра открытого текста + заполнение в шестнадцатеричном формате.

Это покажет вам:

5z0q3xNnokw=
666f6f6261720000

для mcrypt и

1zyqavq7sCk=
666f6f6261720202

для OpenSSL.

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


Первая нулевая начинкаdata тогда и только тогда, когда вход mcrypt не кратен 8 байтам (размер блока Blowfish), тогда используйте OPENSSL_ZERO_PADDING в качестве режима заполнения.

Обратите внимание, что глядя наИсходный код показывает, что OPENSSL_ZERO_PADDING по какой-то неуказанной причине, по-видимому, означает «без заполнения» для оболочки, а OPENSSL_NO_PADDING действительно конфликтует с другими настройками - это я считаю довольно плохимОшибка игнорирования и реализации разработчиками API-оболочки PHP OpenSSL.

Дополнительную информацию можно найти в замечательном исследовании, проведенном Reinier , которое показывает, как API блокирует / распаковывает (или забываетpad / unpad, в зависимости от того, где вы стоите).

...