Moving from mcrypt with Blowfish & ECB to OpenSSL

后端 未结 1 1510
不知归路
不知归路 2021-01-18 19:54

In the (not too distant) past a decision has been made (by someone who longer works here) to always \'encrypt\' database IDs to something else, on the fly, whenever it was n

相关标签:
1条回答
  • 2021-01-18 20:32

    It is very tricky. You can just use the code.

    # cat a.php
    <?php
    function mcrypt_blowfish_encrypt_hex($key, $str)
    {
        $encrypted = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $str, MCRYPT_MODE_ECB);
        return bin2hex($encrypted);
    }
    
    function make_openssl_blowfish_key($key)
    {
        if("$key" === '')
            return $key;
    
        $len = (16+2) * 4;
        while(strlen($key) < $len) {
            $key .= $key;
        }
        $key = substr($key, 0, $len);
        return $key;
    }
    
    function openssl_blowfish_encrypt_hex($key, $str)
    {
        $blockSize = 8;
        $len = strlen($str);
        $paddingLen = intval(($len + $blockSize - 1) / $blockSize) * $blockSize - $len;
        $padding = str_repeat("\0", $paddingLen);
        $data = $str . $padding;
        $key = make_openssl_blowfish_key($key);
        $encrypted = openssl_encrypt($data, 'BF-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
        return bin2hex($encrypted);
    }
    
    function openssl_blowfish_decrypt_hex($key, $hex)
    {
        $key = make_openssl_blowfish_key($key);
        $decrypted = openssl_decrypt(hex2bin($hex), 'BF-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
        return rtrim($decrypted, "\0");
    }
    
    
    function test()
    {
        for($i = 1; $i < 32; $i++) {
            for($j = 1; $j < 32; $j++) {
                $key = str_repeat('' . rand(0, 9), $j);
                $str = str_repeat('' . rand(0, 9), $i);
    
                $encoded_openssl = openssl_blowfish_encrypt_hex($key, $str);
                $decoded_openssl = openssl_blowfish_decrypt_hex($key, $encoded_openssl);
                if($decoded_openssl != $str)
                    die("encrypt($key, $str) wrong: $encoded_openssl: decrypt failed\n");
    
    
                if(function_exists('mcrypt_encrypt')) {
                    $encoded_mcrypt = mcrypt_blowfish_encrypt_hex($key, $str);
                    if($encoded_openssl != $encoded_mcrypt)
                        die("encrypt($key, $str) wrong: $encoded_openssl, mcrypt=$encoded_mcrypt\n");
                }
    
                echo "key='$key', str='$str', encrypted='$encoded_openssl'\n";
            }
        }
    }
    
    echo "openssl: thisismyitemyes:" . openssl_blowfish_encrypt_hex('thisismyitemyes', serialize('6918')) . "\n";
    echo "openssl: headphone:" . openssl_blowfish_encrypt_hex('headphone', serialize('581856')) . "\n";
    
    test();
    

    And run, it works:

    # php a.php
    openssl: thisismyitemyes:b192ac0f6105416a710aec3ce92b1085
    openssl: headphone:ef057c036eb024865406838c62590a93
    key='7', str='3', encrypted='945b638624ecbd5e'
    key='22', str='1', encrypted='3daf096bdc744d8a'
    key='888', str='0', encrypted='b164bb0b603f439e'
    key='2222', str='9', encrypted='d3458df30aef0b4b'
    ...
    ...
    key='3333333333333333333333333333333', str='11111111111111111111111111111', encrypted='b0c9bf45d6f5c7b3b0c9bf45d6f5c7b3b0c9bf45d6f5c7b363a25777c712f1d5'
    key='4444444444444444444444444444444', str='999999999999999999999999999999', encrypted='dd6aaf466121c0f6dd6aaf466121c0f6dd6aaf466121c0f659a2271369ab6731'
    key='7777777777777777777777777777777', str='3333333333333333333333333333333', encrypted='6591e9cc92a6473a6591e9cc92a6473a6591e9cc92a6473a208a7a562babc60c'
    

    The problems:

    1. IVs are ignored in ECB mode, so just remove all IVs in your code.

    2. Due to bug: https://bugs.php.net/bug.php?id=72362 . In mcrypt, the blowfish key is cycled over by the short key. But in openssl, the blowfish key is zero-padded by the short key. So we need to make a cycled key for openssl to decrypt the mcrypt's encryption.

    3. When you are using zero-padding with openssl (keep the same output of mcrypt), you should do the padding by yourself. Well, I did a trick to get the paddingLen, but it is really easy: just think about how many bytes we should append to make the total length is 0/8/16/24/32/40, etc.

    0 讨论(0)
提交回复
热议问题