用OpenSSL取代Mcrypt

maT*_*aTu 19 php openssl mcrypt

目前我们在我们的系统上有一个mcrypt实现,用于在PHP应用程序中隐藏一些敏感数据.现在我们有了一个新的要求,我们必须将crypt模块更改为openssl.另一件重要的事情是我们使用密码河豚和模式ecb.所以我开始测试有什么区别以及如何使用openssl解密mcrypt加密字符串.

我使用标准的PHP函数:

  • mcrypt_encrypt与openssl_encrypt
  • mcrypt_decrypt与openssl_decrypt

两种方法都会产生不同的结果.第二件事是在两种类型的给定密码(河豚)和模式(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);
Run Code Online (Sandbox Code Playgroud)

这是我的结果:

= 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
Run Code Online (Sandbox Code Playgroud)

也许现在有什么想法?

谢谢!

clo*_*ver 10

Blowfish是分组密码.它要求在加密之前填充数据.OpenSSL使用PKCS#7,mcrypt使用PKCS#5.数据的不同填充算法.最小PKCS#5填充长度为0,对于PKCS#7,它是1(维基百科).看一下这个例子(我手动填充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);

?>
Run Code Online (Sandbox Code Playgroud)

使用mcrypt_encrypt()加密openssl_decrypt()数据是不可能的,除非在mcrypt_encrypt()调用之前使用PKCS#7进行手动数据填充.

在您的情况下只有一种方法 - 重新获取数据.

PS:您的来源有错误 - ECB模式根本不使用IV(维基百科)

  • 谢谢,这个提示帮助我使 Rijndael-128/AES-128 在 MCrypt 和 OpenSSL 之间兼容。但不幸的是,它不适用于 Blowfish - (2认同)
  • @clover PKCS#7 和 PKCS#5 本质上是相同的,并且在实现上也是相同的,只是懒惰的开发人员重新使用了“PKCS#5”标识符,而不是添加 PKCS#7 标识符。请修正答案并发表评论。请参阅 [PKCS#7 填充](https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7):*PKCS#5 填充与 PKCS#7 填充相同,只是它仅为块定义使用 64 位(8 字节)块大小的密码。**实际上,两者可以互换使用**。* (2认同)

小智 6

如果您想使用 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);
Run Code Online (Sandbox Code Playgroud)

使用 mcrypt_decrypt 进行解密就像使用 mcrypt 进行加密一样。

mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv)
Run Code Online (Sandbox Code Playgroud)