Заменете Mcrypt с OpenSSL

в момента имаме внедряване на mcrypt в нашите системи за криптиране на някои разумни данни в нашето PHP приложение. Сега имаме ново изискване, че трябва да сменим криптиращия модул на 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(), освен ако ръчното допълване на данни не е направено с PKCS#7 преди mcrypt_encrypt() да бъде извикано.

Във вашия случай има само един начин - прекриптирайте данните.

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 байта (статията в Wikipedia за 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