Php 用OpenSSL替换Mcrypt

Php 用OpenSSL替换Mcrypt,php,openssl,mcrypt,Php,Openssl,Mcrypt,目前,我们的系统上有一个mcrypt实现,可以在PHP应用程序中加密一些有用的数据。现在我们有一个新的需求,我们必须将crypt模块更改为openssl。另一件重要的事情是,我们正在使用密码河豚和模式ecb。所以我开始测试它们之间的区别,以及如何用openssl解密mcrypt加密的字符串 我使用了标准PHP函数: mcrypt_加密与openssl_加密 mcrypt_解密与openssl_解密 这两种方法产生了不同的结果。第二件事是,在两种类型的给定密码(blowfish)和模式(ecb

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

我使用了标准PHP函数:

  • mcrypt_加密与openssl_加密
  • mcrypt_解密与openssl_解密
这两种方法产生了不同的结果。第二件事是,在两种类型的给定密码(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);
现在有什么想法吗


谢谢

河豚是分组密码。它要求在加密之前对数据进行填充。 OpenSSL使用PKCS#7,mcrypt使用PKCS#5。不同的数据填充算法。 最小PKCS#5填充长度为0,对于PKCS#7,填充长度为1()。看看这个例子(我已经手动填充了PKCS#7风格的
mcrypt_encrypt()
的输入数据):


除非在调用
mcrypt\u encrypt()
之前使用PKCS#7手动填充数据,否则无法打开使用mcrypt\u encrypt()加密的SSL_decrypt()数据

在您的情况下,只有一种方法—重新加密数据


PS:您的源代码中有一个错误-ECB模式根本不使用IV()

对于较短的密钥,您应该在迁移mcrypt的blowfish时为openssl创建循环密钥

openssl_decrypt($data, 'bf-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
函数make_openssl_blowfish_key($key)
{
如果(“$key”==”)
返回$key;
$len=(16+2)*4;
while(strlen($key)<$len){
$key.=$key;
}
$key=substr($key,0,$len);
返回$key;
}
见:


请参阅:

@clover正确地认为,在mcrypt和Openssl之间,河豚的默认填充是不同的,但错误的是,它无法完成。如果使用
OPENSSL\u ZERO\u 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);

如果您想使用openssl加密,但在使用mcrypt解密时仍然得到与使用mcrypt加密相同的结果,则需要在使用openssl_encrypt加密输入字符串之前手动将其填充为null,并传递
openssl_RAW_DATA | openssl_ZERO_PADDING
选项

mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv)
然后,使用mcrypt_decrypt进行解密,就像您也使用mcrypt进行加密一样


您必须运行一个迁移脚本,该脚本使用mcrypt对当前数据进行解密,然后使用openssl再次对其进行加密,否则您需要实现一种方法,通过该方法知道对每个数据项使用哪些加密/解密函数,在下次访问使用mcrypt加密的数据时,根据需要将它们从mcrypt更改为openssl。据我所知,mcrypt和OpenU ssl使用不同的密钥派生方法,因此Jon是对的,您需要通过解密然后加密或标记数据进行迁移,以便在下次访问数据时进行迁移。我想知道为什么在ECB模式下需要IB。请参阅上的ECB模式说明。另请参阅并感谢,此提示帮助我使Rijndael-128/AES-128在MCrypt和OpenSSL之间兼容。不幸的是,它对Blowfish不起作用-事实证明它对Blowfish起作用,但密钥大小必须至少为16字节(Wikipedia Blowfish文章称它支持较少).这是因为PKCS中的填充#7@cloverPKCS#7和PKCS#5本质上是相同的,在实现上也是相同的,只是懒惰的开发人员重新使用了
PKCS#5
标识符,而不是添加PKCS#7标识符。请确定答案和评论。请参阅:PKCS#5填充与PKCS#7填充相同,只是它仅为使用64位(8字节)块大小的块密码定义。实际上,这两者可以互换使用。@clover还请参见::许多加密库使用指示PKCS#5或PKCS#7的标识符来定义相同的填充机制。
OPENSSL_ZERO_padding
不添加任何填充,任何非标准填充都需要在加密之前手动添加,并在解密时删除。从文档中可以看出,您可能正在使用不同的版本。从doc注释中可以看出:因此,OPENSSL_ZERO_PADDING禁用了上下文的填充,这意味着您必须手动将自己的填充应用于块大小。如果不使用OPENSSL_ZERO_PADDING,您将自动获得PKCS#7 PADDING。@zaph也许您应该在否决投票前尝试代码?对我有用。
openssl_decrypt($data, 'bf-ecb', $key, 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_encrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv)