PHP 将 mcrypt 转换为 openssl

Posted

技术标签:

【中文标题】PHP 将 mcrypt 转换为 openssl【英文标题】:PHP Converting mcrypt to openssl 【发布时间】:2019-07-23 16:24:40 【问题描述】:

我知道 3DES 和 MD5 不安全。一旦我再次工作,我将努力更换它们,

我有一个移动应用程序,它使用 3DES 和密钥的 MD5 作为秘密密钥来与 php 应用程序对话。

现在这段代码在 PHP 5.3 上完美运行(这是我生成的一个示例)

mcrypt_decrypt(
    MCRYPT_3DES, 
    md5(
        utf8_encode(
            "MobileAppSecureKey"
        ),
        true
    ), 
    base64_decode("bkCfcseIt/TPsgNCdyX9fv2/4MjOJdaPXakNNbxQT3n6tXHa5bDoXojQ3g7jPLCu+wjwD0guQzw3hCFUSVx47PmDNHASk7g/kJ4K4tX0VGI="), 
    MCRYPT_MODE_CBC, 
    base64_decode("cTOCJ/iYL18=")
)

现在我已经将它移植到使用 OpenSSL 方法,我的新代码是

openssl_decrypt(
    base64_decode("bkCfcseIt/TPsgNCdyX9fv2/4MjOJdaPXakNNbxQT3n6tXHa5bDoXojQ3g7jPLCu+wjwD0guQzw3hCFUSVx47PmDNHASk7g/kJ4K4tX0VGI="), 
    'DES-EDE3-CBC', 
    md5(
        utf8_encode(
            "MobileAppSecureKey"
        ),
        true
    ), 
    0, 
    base64_decode("cTOCJ/iYL18=")
)

但新代码不起作用,它给出了来自openssl_error_string()的错误error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length

我不确定为什么它在 mcrypt 中完美运行时抱怨最终块长度

用于生成测试数据的代码来自移动应用,移动应用是 Cordova 应用,使用 CryptoJS library

key = CryptoJS.MD5(key);

// copy 3DES subkey 1 to the last 64 bit to make a full 192-bit key
key.words[4] = key.words[0];
key.words[5] = key.words[1];

if(typeof(iv) === "undefined")
    iv = CryptoJS.lib.WordArray.random(8);


var encrypted = CryptoJS.TripleDES.encrypt(pt, key, iv: iv);
return "str":encrypted.toString(), "iv":CryptoJS.enc.Base64.stringify(iv);

加密的有效载荷是

"jsonrpc":"2.0","method":"events.enableParentGenres","params":[159],"id":1

已经尝试过的修改,

根据 yivi 的建议,选项已设置为 OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING

根据 Tuckbros 的建议,该消息已被填充

$message_padded = base64_decode("bkCfcseIt/TPsgNCdyX9fv2/4MjOJdaPXakNNbxQT3n6tXHa5bDoXojQ3g7jPLCu+wjwD0guQzw3hCFUSVx47PmDNHASk7g/kJ4K4tX0VGI=");
$message_padded = str_pad($message_padded,
        strlen($message_padded) + 8 - strlen($message_padded) % 8, "\0");

这两者都防止了有关最终块长度的错误,但是在运行代码时加密的有效负载没有解密。

【问题讨论】:

不是移动应用程序使用 JS 库 (CryptoJS) 对其进行加密,它是 Cordova 移动应用程序(更新以显示代码) 和这里不一样? ***.com/questions/41181905/… @Tuckbros 不,我刚刚尝试添加填充,使用该方法解密时消息不起作用是错误的字节 您确定mcrypt_decrypt 示例有效吗?因为 DESCBC 的密钥大小是 24 字节,而 PHP md5 返回一个 16 字节长度的哈希。您的示例产生没有结果的警告“此算法不支持大小为 16 的密钥。仅支持大小为 24 的密钥...”. @MartinBarker 您不必这样做。解决方案是您需要将您拥有的 128 位密钥转换为 192 位密钥,就像在 javascript 代码中一样(将密钥与其前 64 位连接)。另外如果你不想使用选项OPENSSL_RAW_DATA,那么直接传递base64编码的字符串,不要使用base64_decode来解码。 【参考方案1】:

openssl_decrypt 传入的参数好像不对;您将 OPTIONS 参数作为 0 传递,您必须将其设置为 OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,因为您为函数提供了原始数据 (base64_decode)。

密钥还需要转换为 192 位密钥,如 javascript 代码中所示:

$key = md5(utf8_encode("MobileAppSecureKey"), true);

//key.words[4] = key.words[0];
//key.words[5] = key.words[1];

for($i = 0; $i < 8; $i++) 
    $key[$i + 16] = $key[$i];

试试这个:

$key = md5(utf8_encode("MobileAppSecureKey"), true);
$data = base64_decode("bkCfcseIt/TPsgNCdyX9fv2/4MjOJdaPXakNNbxQT3n6tXHa5bDoXojQ3g7jPLCu+wjwD0guQzw3hCFUSVx47PmDNHASk7g/kJ4K4tX0VGI=");

for($i = 0; $i < 8; $i++) 
    $key[$i + 16] = $key[$i];


$decoded = openssl_decrypt(
    $data,
    'DES-EDE3-CBC',
    $key,
    OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
    base64_decode("cTOCJ/iYL18=")
);

echo "\$decoded = $decoded";

// Will output:
// $decoded = "jsonrpc":"2.0","method":"events.enableParentGenres","params":[159],"id":1

【讨论】:

以上是关于PHP 将 mcrypt 转换为 openssl的主要内容,如果未能解决你的问题,请参考以下文章

在 PHP 中将 mcrypt_encrypt 转换为 openssl_encrypt

将 mcrypt AESencrypt 函数转换为 openssl 但不起作用

PHP:OpenSSL 等价于 mcrypt:MCRYPT_3DES?

将此php加密函数转换为python

PHP mcrypt 到 Python

CryptoJS 中 CFB 模式中的 mcrypt_encrypt 函数