dreamfly0514 2018-10-05 19:53
浏览 144
已采纳

无法在PHP中将mcrypt升级为openssl解密

I'm upgrading a legacy application from PHP 7.0 to 7.2 and my decrypt function isn't working when I switch out mcrypt for openssl.

I tried the existing answers like mcrypt is deprecated, what is the alternative?, and Gists like https://gist.github.com/odan/c1dc2798ef9cedb9fedd09cdfe6e8e76, but I'm still not able to make the code work.

Can anyone shed some light what I'm doing wrong?

Old code

function decrypt($value, $key) {
    $ivLength = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $iv = substr($value, 0, $ivLength);
    return rtrim(
        mcrypt_decrypt(
            MCRYPT_RIJNDAEL_128,
            hash('sha256', $key, true),
            substr($value, $ivLength),
            MCRYPT_MODE_CBC,
            $iv
        ),
        "\0"
    );
}

New code (not working with existing inputs)

function decrypt($value, $key) {
    $ivLength = openssl_cipher_iv_length('AES-128-CBC');
    $iv = substr($value, 0, $ivLength);
    // Note: $key is hashed because it was hashed in the old encrypt function below
    return openssl_decrypt(substr($value, $ivLength), 'AES-128-CBC', hash('sha256', $key, true), OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
}

For context, here's how the old code encrypted the values:

function encrypt($value, $key) {
    $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_DEV_URANDOM);
    return $iv . mcrypt_encrypt(
            MCRYPT_RIJNDAEL_128,
            hash('sha256', $key, true),
            $value,
            MCRYPT_MODE_CBC,
            $iv
        );
}

Also, the raw value in $value is twice as long when output to the error log in the new code than the old code and looks similar but with more characters.

  • 写回答

1条回答 默认 最新

  • doumu4916 2018-10-05 20:46
    关注

    The main issue here is the key size. You're creating the key with SHA256 which returns a 256 bit hash, therefore you're using AES / Rijndael with a 256 bit key.

    The number in Rijndael-128 defines the block size, the key size is determined by the length of the key we use. The number in AES-128 defines the key size (the block size is constant, 128 bits), and if the actual key length is different than this number, then the key is shortened or expanded (with zero bytes) to fit the selected key size.

    This means that your mcrypt code uses Rijndael-128 (AES) with a 256 bit key, in CBC mode. The openssl equivalent is AES-256-CBC, and if we use this algorithm then mcrypt and openssl should produce compatible results.

    function decrypt_mcrypt_with_openssl($value, $key) {
        $iv = substr($value, 0, 16);
        $ciphertext = substr($value, 16);
        $key = hash('sha256', $key, true);
        $plaintext = openssl_decrypt(
            $ciphertext, 'AES-256-CBC', $key, 
            OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, 
            $iv
        );
        return rtrim($plaintext, "\0");
    }
    

    SHA256 is not suitable as a key derivation function. If you're deriving your key from a password you can use hash_pbkdf2 with a random salt and a high enough number of iterations. Or you could create a cryptographically secure pseudo-random key with openssl_random_pseudo_bytes.

    I think it's best to stop using mcrypt completely and use only openssl. Mcrypt doesn't support PKCS7 padding, it doesn't provide any authenticated encryption algorithms, and it is not being maintained anymore.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 如何推断此服务器配置
  • ¥15 关于github的项目怎么在pycharm上面运行
  • ¥15 内存地址视频流转RTMP
  • ¥100 有偿,谁有移远的EC200S固件和最新的Qflsh工具。
  • ¥15 找一个QT页面+目标识别(行人检测)的开源项目
  • ¥15 有没有整苹果智能分拣线上图像数据
  • ¥20 有没有人会这个东西的
  • ¥15 cfx考虑调整“enforce system memory limit”参数的设置
  • ¥30 航迹分离,航迹增强,误差分析
  • ¥15 Chrome Manifest扩展引用Ajax-hook库拦截请求失败