代码之家  ›  专栏  ›  技术社区  ›  Björn Tantau

将带有Blowfish和ECB的mcrypt迁移到OpenSSL

  •  3
  • Björn Tantau  · 技术社区  · 6 年前

    我一辈子都搞不懂如何将遗留的mcrypt代码迁移到OpenSSL。我让它为河豚与CBC和Rijndael与CBC工作,但河豚与欧洲央行正在躲避我。

    是的,我读过 Moving from mcrypt with Blowfish & ECB to OpenSSL

    这是我的密码:

    <?php
    function encrypt_with_mcrypt($data, $key) {
            return mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB);
    }
    
    function encrypt_with_openssl($data, $key) {
            return openssl_encrypt($data, 'BF-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY);
    }
    
    $data = 'foobar';
    $key = 'supersecretkey';
    
    var_dump(base64_encode(encrypt_with_mcrypt($data, $key)));
    var_dump(base64_encode(encrypt_with_openssl($data, $key)));
    

    这是输出:

    test.php:13:
    string(12) "5z0q3xNnokw="
    test.php:14:
    string(12) "1zyqavq7sCk="
    
    2 回复  |  直到 6 年前
        1
  •  3
  •   Maarten Bodewes    6 年前

    mcrypt库/包装器默认为零字节填充(仅在需要时),而OpenSSL库/包装器默认为PKCS#5填充。这意味着单个块的填充方式不同,因此将显示不同的密文块。


    一个常见的技巧是解密产生的密文 没有 任何不匹配项,然后通过查看纯文本+十六进制填充来检查填充字节。

    这将向您展示:

    5z0q3xNnokw=
    666f6f6261720000
    

    对于mcrypt和

    1zyqavq7sCk=
    666f6f6261720202
    

    对于OpenSSL。

    使用一个需要加密多个块的更大的纯文本消息,也会向您显示加密正在顺利进行 除了


    先把数据归零 当且仅当 mcrypt输入不是8字节的倍数(Blowfish的块大小),那么使用 OPENSSL_ZERO_PADDING

    注意,查看源代码可以发现 由于某些未指明的原因,似乎意味着包装器和 OPENSSL_NO_PADDING 似乎与其他设置冲突-我认为这是PHP OpenSSL包装器API开发人员的一个相当糟糕的设计和实现错误。

    有关更多信息,请访问 the great research performed by Reinier 这显示了API如何填充/取消填充(或者忘记填充/取消填充,具体取决于您所处的位置)。

        2
  •  2
  •   Reinier Torenbeek    6 年前

    Maarten's answer ,只是我觉得最好能展示一些代码来说明他的话。

    mcrypt

    $key = "supersecretkey";
    $data = "foobar";
    $ctxt = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB);
    $ptxt = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $ctxt, MCRYPT_MODE_ECB);
    echo bin2hex($data).PHP_EOL;
    echo bin2hex($ptxt).PHP_EOL;
    

    提供以下十六进制转储:

    666f6f626172
    666f6f6261720000
    

    openssl

    $key = "supersecretkey";
    $data = "foobar";
    $opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY;
    $ctxt = openssl_encrypt($data, 'BF-ECB', $key, $opts);
    $ptxt = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $ctxt, MCRYPT_MODE_ECB);
    echo bin2hex($data).PHP_EOL;
    echo bin2hex($ptxt).PHP_EOL;
    

    给予

    666f6f626172
    666f6f6261720202
    

    麦克里普 可以通过手动添加填充字节来保持一致。注意 OPENSSL_ZERO_PADDING 选项和添加 "\0\0" :

    $key = "supersecretkey";
    $data = "foobar";
    $ctxt_mc = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB);
    $opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY | OPENSSL_ZERO_PADDING;
    $ctxt_os = openssl_encrypt($data."\0\0", 'BF-ECB', $key, $opts);
    echo bin2hex($ctxt_mc).PHP_EOL;
    echo bin2hex($ctxt_os).PHP_EOL;
    

    e73d2adf1367a24c
    e73d2adf1367a24c
    

    麦克里普

    $key = "supersecretkey";
    $data = "foobar";
    $ctxt_mc = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data."\2\2", MCRYPT_MODE_ECB);
    $opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY;
    $ctxt_os = openssl_encrypt($data, 'BF-ECB', $key, $opts);
    echo bin2hex($ctxt_mc).PHP_EOL;
    echo bin2hex($ctxt_os).PHP_EOL;
    

    d73caa6afabbb029
    d73caa6afabbb029
    

    最后,尝试调用 openssl_encrypt() 禁用填充且长度不是块大小的倍数时:

    $key = "supersecretkey";
    $data = "foobar";
    $opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY | OPENSSL_ZERO_PADDING;
    $ctxt = openssl_encrypt($data, 'BF-ECB', $key, $opts);
    echo(openssl_error_string().PHP_EOL)
    

    给予

    error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:data not multiple of block length
    

    备注:姓名 OPENSSL\零\填充 很混乱,但它的意思是“没有填充”。你可能想用旗子 OPENSSL_NO_PADDING openssl\u encrypt() . 它的价值是 3 ,与 OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING . 相反, it is intended for use with asymmetric cryptography

    推荐文章