【问题标题】:Migrate PHP AES encryption from mcrypt to openssl return different encrypted string将 PHP AES 加密从 mcrypt 迁移到 openssl 返回不同的加密字符串
【发布时间】:2020-11-21 10:12:28
【问题描述】:

我正在尝试将使用 mcrypt 的现有加密功能更改为 OpenSSL。但它会创建不同的加密字符串。我们该如何解决这个问题?

<?php
$str    = 'test';
$method = 'AES-128-CBC';
$key    = 'o6xSYYAVl2eapPI2';
$iv     = 'fedcba9876543210';

function encrypt_mcrypt($str=NULL,$key,$iv) {
  $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'cbc', $iv);

  mcrypt_generic_init($td,$key,$iv);
  $encrypted = mcrypt_generic(@$td,@$str);

  mcrypt_generic_deinit($td);
  mcrypt_module_close($td);

  return bin2hex(@$encrypted);
}
function encrypt_openssl($str=NULL,$key,$iv) {
  $encrypted =openssl_encrypt($str, 'AES-256-CBC', $key, OPENSSL_RAW_DATA,$iv); 
  return bin2hex(@$encrypted);
}


echo 'Mcrypt:'.encrypt_openssl($str,$key,$iv);
echo '<br/>';
echo 'Openssl:'.encrypt_mcrypt($str,$key,$iv);

输出

Mcrypt:0ab40f383b421ba465c0cbbcded97319
Openssl:57c86f3089535b3acfbe65cecbb662b9

【问题讨论】:

    标签: openssl mcrypt


    【解决方案1】:

    两种代码都使用不同的 AES 变体和填充。 mcrypt 代码应用 AES-128 和零填充,openssl 代码应用 AES-256 和 PKCS7 填充。为确保两个密文匹配,两个代码必须使用相同的 AES 变体和填充。

    mcrypt 从密钥大小中识别 AES 变体。由于$key 是一个 16 字节的密钥,因此使用 AES-128。 openssl 根据第二个参数中传递的规范确定 AES 变体。太短的键用 0 值填充到所需的长度,太长的键被截断。这里指定了AES-256-CBC,即。 e.使用 AES-256。 16 字节的键 $key 因此用 0 值填充并扩展为 32 字节的大小。

    mcrypt 隐式使用零填充进行加密,在解密期间不会隐式删除。不支持 PKCS7 填充。 openssl 隐式应用 PKCS7 填充进行加密,在解密期间被隐式删除。不支持零填充。如果 openssl 代码应该使用零填充或 mcyrpt 代码 PKCS7 填充,则必须自己实现。

    关于从 mcryptopenssl 的迁移,openssl 代码在下面进行了修改,在功能上与 mcrypt 代码,即使用 AES-128 和零填充。对于 AES 变体,只有规范 AES-256-CBC 必须更改为 AES-128-CBC。关于填充,必须使用 OPENSSL_ZERO_PADDING 禁用默认的 PKCS7 填充,并且必须实现零填充本身(请注意,OPENSSL_ZERO_PADDING 标志仅禁用填充,但不启用零填充;名称选择不当):

    <?php
    $str    = "test";
    $key    = 'o6xSYYAVl2eapPI2';
    $iv     = 'fedcba9876543210';
    
    function encrypt_openssl($str = NULL, $key, $iv) {
    
        $encrypted = openssl_encrypt(
            zeroPad($str, 16),                         // Zero pad plaintext
            'AES-128-CBC',                             // Choose AES-128 
            $key, 
            OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,   // Disable PKCS7 Padding
            $iv); 
         
        return bin2hex(@$encrypted);
    }
    
    function zeroPad($text, $bs) {
        $pad = $bs - strlen($text) % $bs;
        return ($pad < 16) ? $text .  str_repeat("\0", $pad) : $text;
    }
    
    echo 'Openssl:'.encrypt_openssl($str,$key,$iv); // Openssl:57c86f3089535b3acfbe65cecbb662b9
    

    为了与您的结果进行比较,请注意您混淆了输出的标签,即 openssl 结果标记为 Mcrypt,反之亦然!

    最后一点:一般来说,PKCS7 填充比零填充更可靠,因为前者包含填充长度的信息。零填充不是这种情况,因此在删除填充时(即解密后),无法区分常规字节和填充字节。还有不同的零填充变体,例如如果明文的长度已经对应于块大小的整数倍(此变体使用 mcrypt),则一个不填充,在这种情况下,另一个填充一个完整的块。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-02
      • 2013-05-29
      • 1970-01-01
      • 2014-06-28
      • 1970-01-01
      • 2014-09-12
      • 1970-01-01
      • 2011-02-23
      相关资源
      最近更新 更多