使用openssl解密mcrypt

时间:2015-07-20 15:20:55

标签: php encryption openssl cryptography mcrypt

由于mcrypt被认为已过时,我的任务是升级当前代码以使用openssl。听起来很简单,但......经过几天的尝试和失败后,我觉得自己很疯狂。

我的问题是:你有什么方法可以用之前用mcrypt加密的openssl数据解密吗?我已经阅读了很多这方面的帖子,大多数人都说在运行mcrypt之前需要先前手动填充数据。 问题是mcrypt-ed数据已经加密(使用mcrypt提供的自动空填充)并驻留在数据库中,因此无法对此进行修改。

提及:

  1. 使用的算法是rijndael-128 cbc,带有32字节密钥(因此我使用aes-256-cbc作为openssl)。
  2. 我正在使用php(php-crypto)的openssl包装器。
  3. 我设法使逆操作工作(使用mcrypt解码openssl),只需将末尾解码后的字符解析为非字母数字。
  4. 在mcrypting之前手动填充数据然后使用openssl解密它就像魅力一样,但这不是问题所在。
  5. 部分代码段:

    // Simple mcrypt encrypt, decrypt with php-crypto example
    // This doesn't work and produces a "Finalizing of cipher failed" error
            $data = "This is a text";
            $strMcryptData=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
    
            $algorithm = 'aes-256-cbc';
            $cipher = new Cipher($algorithm);
            $sim_text = $cipher->decrypt($strMcryptData, $key, $iv);
    
    // Simple mcrypt encrypt with padding, decrypt with php-crypto
    // Works and produces the correct text on decryption
            $pad =  $blocksize - (strlen($data) % $blocksize);
            $text = $data;
            $text .= str_repeat(chr($pad), $pad);
            $strPaddedData=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);
    
            $sim_text = $cipher->decrypt($strPaddedData, $key, $iv);
    

3 个答案:

答案 0 :(得分:5)

如果你在没有手动添加PKCS7的情况下在mcrypt中加密,mcrypt将很乐意用NUL字节填充你的明文。

每当使用aes-X-cbc时,

OpenSSL将为您执行PKCS7填充 。这样做的不幸后果是,如果您有AES-CBC(NULL_PADDED(plaintext))并尝试解密它,openssl_decrypt将尝试删除填充并失败。

比较http://3v4l.org/bdQe9http://3v4l.org/jr68fhttp://3v4l.org/K6ZEU

OpenSSL扩展目前没有为您提供一种方法来说明“此字符串未填充,请不要为我删除填充”,然后自行删除NUL字节。您必须使用PKCS7填充进行加密,以便解密成功。

虽然这是OpenSSL的限制,但它强调的是,你遇到它的唯一原因是因为mcrypt is terrible

答案 1 :(得分:5)

稍微过时,但你可以通过一些工作来解决这个问题。你可以告诉PHP的OpenSSL加密字符串没有填充,并告诉它给你原始输出(所以你也不必对它进行base64解码)。然后你可以从结果字符串的末尾去掉空值,如果字符串的长度碰巧可以完全被IV整除(这是一个完整性检查,好像结果字符串不能被IV整除然后它不是& #39; t填充)。

请注意,此代码有两个主要限制

  1. 如果您在任何时候加密了以两个或更多NULL个字节结尾的合法字符串,那么此代码将不会为您提供相同的输出。

  2. 如果字符串的填充只需要一个空字节,则此代码不会剥离它。

  3. 如果你知道 FACT 你没有加密以空字节结尾的任何东西,你可以解决这两个问题,你可以改变剥离空值的代码来做一个preg_replace函数;只需确保将正则表达式锚定到字符串的末尾,以便它只从末尾剥离。

    http://3v4l.org/kYAXn

    显然这段代码没有主要的免责声明,请在您的用例中进行测试,但有人可能会发现这有用。

答案 2 :(得分:2)

对于填充,除了之外不应存在任何重大差异。如果直接使用更高级别的OpenSSL(EVP)构造,则应该能够调用EVP_CIPHER_CTX_set_padding。我认为padding参数应为零,尽管it is not documented。您需要一个预先配置的加密/解密上下文。

之后,您将获得与密文长度相同的明文。末尾的0到15个字节将设置为零。您需要手动删除这些字节。如果明文恰好以零字节结束,那么这些也将被删除;然而,如果明文是可打印的字符串(使用8位编码),情况绝非如此。您可能希望确保不会删除超过15个字节。

如果您获得完全随机的明文,那么您的密钥或密文不正确。如果您获得可读的纯文本但前16个字节,那么您的IV处理是不正确的。