Замените Mcrypt на OpenSSL

в настоящее время у нас есть внедрение mcrypt в наши системы для шифрования некоторых разумных данных в нашем приложении PHP. Теперь у нас есть новое требование: мы должны изменить модуль crypt на openssl. Еще одна вещь, которую важно знать, это то, что мы используем шифр Blowfish и режим ecb. Поэтому я начал проверять, в чем различия и как я могу расшифровать зашифрованные строки mcrypt с помощью openssl.

Я использовал стандартную функцию PHP:

  • mcrypt_encrypt против openssl_encrypt
  • mcrypt_decrypt против openssl_decrypt

Оба метода дают разные результаты. Во-вторых, в данном шифре (blowfish) и режиме (ecb) в обоих типах требуются разные длины IV (openssl=0 и mcrypt=56).

Кто-нибудь знает, как я могу легко изменить модули без больших усилий по миграции?

Заранее спасибо!

ОБНОВИТЬ:

Вот код, который я тестировал:

<?php 

function say($message){
    if(!is_string($message)){
        if(!isset($_SERVER["HTTP_USER_AGENT"])) echo "<pre>";
        echo var_export($message, true) . ((!isset($_SERVER["HTTP_USER_AGENT"]) ? "\n" : "<br />"));
        if(!isset($_SERVER["HTTP_USER_AGENT"])) echo "</pre>";
    }else{
        echo $message . ((!isset($_SERVER["HTTP_USER_AGENT"]) ? "\n" : "<br />"));
    }
}

say("= Begin raw encryption");
$key    = "anotherpass";
$str    = "does it work";

say("  Params:");
say("  - String to encrypt '".$str."'");
say("  - Key: ".$key);
say("");


$params = array(
    "openssl"  => array(
        "cipher"    => "BF",
        "mode"      => "ECB",
    ),
    "mcrypt" => array(
        "cipher"    => "blowfish", 
        "mode"      => "ecb",
    ),
);

say("= Mcrypt");
$handler = mcrypt_module_open($params['mcrypt']['cipher'], '', $params['mcrypt']['mode'], '');
$iv      = mcrypt_create_iv (mcrypt_enc_get_iv_size($handler), MCRYPT_RAND);
$keysize = mcrypt_enc_get_key_size($handler);
mcrypt_generic_init($handler,$key,"\0\0\0\0\0\0\0\0");
say("  Params:");
say("  - InitVector   ".bin2hex($iv)." (bin2hex)");
say("  - Max keysize  ".$keysize);
say("  - Cipher       ".$params['mcrypt']['cipher']);
say("  - Mode         ".$params['mcrypt']['mode']);
say("");
say("  Encryption:");
$m_encrypted = mcrypt_generic($handler, $str);
$m_decrypted = mdecrypt_generic($handler, $m_encrypted);
say("  - Encrypted   ".bin2hex($m_encrypted)." (bin2hex)");
say("  - Descrypted  ".$m_decrypted);
say("");


say("= Openssl");
say("  Params:");
say("  - InitVector   not needed");
say("  - Max keysize  ".openssl_cipher_iv_length($params['openssl']['cipher']."-".$params['openssl']['mode']));
say("  - Cipher       ".$params['openssl']['cipher']);
say("  - Mode         ".$params['openssl']['mode']);
say("");
say("  Encryption:");
$o_encrypted = openssl_encrypt($str,$params['openssl']['cipher']."-".$params['openssl']['mode'],$key,true);
$o_decrypted = openssl_decrypt($o_encrypted,$params['openssl']['cipher']."-".$params['openssl']['mode'],$key,true);
say("  - Encrypted   ".bin2hex($o_encrypted)." (bin2hex)");
say("  - Descrypted  ".$o_decrypted);

И это мой результат:

= Begin raw encryption
  Params:
  - String to encrypt 'does it work'
  - Key: anotherpass

= Mcrypt
  Params:
  - InitVector   06a184909d7bf863 (bin2hex)
  - Max keysize  56
  - Cipher       blowfish
  - Mode         ecb

  Encryption:
  - Encrypted   0e93dce9a6a88e343fe5f90d1307684c (bin2hex)
  - Descrypted  does it work

= Openssl
  Params:
  - InitVector   not needed
  - Max keysize  0
  - Cipher       BF
  - Mode         ECB

  Encryption:
  - Encrypted   213460aade8f9c14d8d51947b8231439 (bin2hex)
  - Descrypted  does it work

Может есть идеи сейчас?

Спасибо!


person maTu    schedule 03.04.2012    source источник
comment
Вам нужно либо запустить сценарий миграции, который расшифровывает текущие данные с помощью mcrypt, а затем снова шифрует их с помощью openssl, либо вам нужно будет реализовать метод, с помощью которого вы знаете, какие функции шифрования/дешифрования использовать для каждого элемента данные и измените их с mcrypt на openssl по мере необходимости при следующем доступе к некоторым данным, зашифрованным с помощью mcrypt.   -  person Jon    schedule 22.08.2012
comment
Насколько я понимаю, mcrypt и open_ssl используют разные методы получения ключей, поэтому Джон прав, вам нужно будет либо выполнить миграцию путем расшифровки, а затем зашифровать или пометить данные, чтобы они переносились при следующем доступе.   -  person Reid Johnson    schedule 31.10.2012
comment
Интересно, зачем нужен IB в режиме ECB. См. описание режима ECB на странице en.wikipedia.org/wiki/Block_cipher_modes_of_operation.   -  person doptimusprime    schedule 06.04.2013


Ответы (4)


Blowfish — это блочный шифр. Это требует, чтобы данные были дополнены перед шифрованием. OpenSSL использует PKCS#7, а mcrypt использует PKCS#5. Различные алгоритмы заполнения данных. Минимальная длина заполнения PKCS#5 — 0, для PKCS#7 — 1 (wikipedia). Взгляните на этот пример (я вручную дополнил входные данные для mcrypt_encrypt() в стиле PKCS#7):

<?php 

$key = "anotherpassword1";
$str = "does it work 12";

$enc = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $str."\1", MCRYPT_MODE_ECB);
$dec = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $enc, MCRYPT_MODE_ECB);
echo(bin2hex($enc).PHP_EOL);
var_dump($dec);

$enc = openssl_encrypt($str, 'bf-ecb', $key, true);
$dec = openssl_decrypt($enc, 'bf-ecb', $key, true);
echo(bin2hex($enc).PHP_EOL);
var_dump($dec);

?>

Данные openssl_decrypt(), зашифрованные с помощью mcrypt_encrypt(), невозможно, если до вызова mcrypt_encrypt() не было выполнено ручное заполнение данных с помощью PKCS#7.

В вашем случае есть только один способ - перешифровать данные.

PS: В вашем исходнике ошибка - режим ECB вообще не использует IV (wikipedia)

person clover    schedule 03.11.2013
comment
Спасибо, этот совет помог мне сделать Rijndael-128/AES-128 совместимым между MCrypt и OpenSSL. К сожалению, он не работает для Blowfish - - person Narf; 30.01.2014
comment
Оказывается, это работает для Blowfish, НО размер ключа должен быть не менее 16 байт (в статье Википедии для Blowfish говорится, что он поддерживает меньше). - person Narf; 05.02.2014
comment
Это из-за заполнения в PKCS # 7 - person clover; 09.02.2014
comment
Заполнение применяется к обычным текстовым данным, я говорю о ключе. Тем не менее, +1 за полезный ответ. :) - person Narf; 10.02.2014
comment
@Narf - звучит как ошибка, если ключ Blowfish размером 4 байта отклоняется, поскольку размер ключа составляет от 32 до 448 бит. - person jww; 16.02.2017
comment
@clover PKCS # 7 и PKCS # 5 по сути одинаковы и одинаковы в реализации, просто ленивые разработчики повторно использовали идентификатор PKCS#5 вместо добавления идентификатора PKCS # 7. Пожалуйста, исправьте ответ и прокомментируйте. См. заполнение PKCS#7: заполнение PKCS#5 идентично к заполнению PKCS#7, за исключением того, что оно было определено только для блочных шифров, которые используют 64-битный (8-байтовый) размер блока. На практике они могут использоваться взаимозаменяемо. - person zaph; 10.03.2018
comment
@clover См. также: В чем разница между дополнением PKCS#5 и дополнением PKCS#7: Многие криптографические библиотеки используют идентификатор, указывающий PKCS#5 или PKCS#7, для определения одного и того же механизма заполнения. - person zaph; 10.03.2018

Если вы хотите зашифровать с помощью openssl и получить тот же результат, как если бы вы зашифровали его с помощью mcrypt при расшифровке с помощью mcrypt, вам необходимо вручную дополнить входную строку нулями перед шифрованием с помощью openssl_encrypt и передать параметры OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING.

$str = 'encrypt me';
$cipher = 'AES-256-CBC';
$key = '01234567890123456789012345678901';
$opts = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING;
$iv_len = 16;
$str_len = mb_strlen($str, '8bit');
$pad_len = $iv_len - ($str_len % $iv_len);
$str .= str_repeat(chr(0), $pad_len);
$iv = openssl_random_pseudo_bytes($iv_len);


$encrypted = openssl_encrypt($str, $cipher, $key, $opts, $iv);

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

mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv)
person daiwai    schedule 08.07.2018

Для более коротких ключей вы должны сделать зацикленные ключи для openssl при переносе blowfish mcrypt.

function make_openssl_blowfish_key($key)
{
    if("$key" === '')
        return $key;

    $len = (16+2) * 4;
    while(strlen($key) < $len) {
        $key .= $key;
    }
    $key = substr($key, 0, $len);
    return $key;
}

См.: https://bugs.php.net/bug.php?id=72362< /а>

См.: Переход с mcrypt с помощью Blowfish и ECB на OpenSSL< /а>

person shawn    schedule 08.03.2018

@clover прав в том, что заполнение по умолчанию для Blowfish отличается от mcrypt и Openssl, но ошибается, что это невозможно сделать. Если вы используете опцию OPENSSL_ZERO_PADDING для расшифровки, они фактически совместимы:

openssl_decrypt($data, 'bf-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
person ColinM    schedule 09.03.2018
comment
OPENSSL_ZERO_PADDING не добавляет никакого заполнения, любое нестандартное дополнение необходимо будет добавить вручную перед шифрованием и удалить при расшифровке. Судя по документации openssl_encrypt, возможно, вы используете другую версию . Из комментариев к документу: Итак, OPENSSL_ZERO_PADDING отключает отступы для контекста, а это означает, что вам придется вручную применять собственные отступы к размеру блока. Без использования OPENSSL_ZERO_PADDING вы автоматически получите заполнение PKCS#7. - person zaph; 10.03.2018
comment
@zaph Может быть, вам стоит попробовать код, прежде чем понизить голос? Работает на меня. - person ColinM; 13.03.2018