NodeJS Crypto TripleDes 解密(mcrypt 端口)

Posted

技术标签:

【中文标题】NodeJS Crypto TripleDes 解密(mcrypt 端口)【英文标题】:NodeJS Crypto TripleDes decryption (mcrypt port) 【发布时间】:2021-06-19 13:23:58 【问题描述】:

我正在为一些用 php 5.5 和 mcrypt 编写的遗留代码而苦苦挣扎。 我想在 Node.js 中创建一个向后兼容的功能,因此我必须将下面的代码移植到更新的标准。

public function decr($hash) 
        $decoded = base64_decode($hash);

        $decodedShorter = substr($decoded, 0, -8);
        $iv = substr($decoded, -8);
        $decr = rtrim(@mcrypt_decrypt(MCRYPT_3DES, file_get_contents('some.key'), $decodedShorter, MCRYPT_MODE_CFB, $iv));

        return $decr;
    

我一直在尝试多种策略,包括节点引擎之外的 crypto-js 和原生 crypto

我遇到的最新问题:

ERR_CRYPTO_INVALID_IV


const decrypt = (text, secretKey, iv = null) => 
    const decipher = crypto.createDecipheriv('des-ede3-cfb8', secretKey, iv);
    let decrypted = decipher.update(text, 'utf8');
    decrypted += decipher.final();
    return decrypted;
;

async function main() 
    const decoded = atob(name);
    const key = await readFile(
        path.resolve(`some.key`)
    )
    
    const decodedShorter = decoded.substr(0, decoded.length - 8)
    const iv = decoded.substr(-8)
    
    
    return decrypt(decodedShorter, key, Buffer.from(iv))

有什么想法吗?新的 openSSL 实现与 mcrypt 的实现有何不同以至于不兼容?或者,也许我搞砸了什么?我很确定参数类型是正确的,因为我指的是@types/node/crypto,但是内容/逻辑本身有一些不正确的地方......

【问题讨论】:

OpenSSL 适用于 PHP 5.5,但也请停止使用 PHP5。 【参考方案1】:

PHP代码中的decr()方法先Base64对加密数据进行解码,然后将密文和IV分离。这里预计将 8 字节 IV 附加到密文中。 之后,在 CFB 模式下使用 AES 进行解密。有不同段大小的不同 CFB 变体,这里使用 8 位的段大小。 CFB 是一种流密码模式,因此不需要/应用填充。

已发布的 NodeJS 代码中的错误是密文和 IV 使用 UTF-8 编码作为字符串处理。这通常会破坏任意字节序列(例如密文或 IV)。 关于密文,损坏发生在decipher.update(text, 'utf8')。这里 UTF-8 在第二个参数中明确指定为输入编码。 关于 IV,将 IV 读入缓冲区时会发生损坏:Buffer.from(iv)。由于在第二个参数中没有指定编码,因此隐式使用 UTF-8。这两个问题都可以通过使用latin1 作为编码来解决。

更强大的解决方案是始终使用缓冲区,这样就不需要编码:

var crypto = require('crypto')

const decrypt = (text, secretKey, iv = null) => 
    const decipher = crypto.createDecipheriv('des-ede3-cfb8', secretKey, iv);
    let decrypted = decipher.update(text, '', 'utf8'); 
    decrypted += decipher.final('utf8');
    return decrypted;


const name = "OrjgCsq9EkT2TkCZzDOfW492nXQCNIC0BtVJy1FaaTXv2jXAPqx75kaUJVSG/5MCFXXq"
const decoded = Buffer.from(name, 'base64')
const decodedShorter = decoded.slice(0, decoded.length - 8)
const iv = decoded.slice(decoded.length - 8)
const key = Buffer.from('ffa3b5205582d6ea7de6439ec2bafef46a80810003158922', 'hex');
console.log(decrypt(decodedShorter, key, iv))

测试: 两个代码都使用密钥$key 将以下密文$ciphertext 解密为给定的明文:

$ciphertext = 'OrjgCsq9EkT2TkCZzDOfW492nXQCNIC0BtVJy1FaaTXv2jXAPqx75kaUJVSG/5MCFXXq';
$key = hex2bin('ffa3b5205582d6ea7de6439ec2bafef46a80810003158922');
// Plaintext: The quick brown fox jumps over the lazy dog  

【讨论】:

以上是关于NodeJS Crypto TripleDes 解密(mcrypt 端口)的主要内容,如果未能解决你的问题,请参考以下文章

前端 使用 crypto-js 对数据进行对称加密

javax.crypto.BadPaddingException:给定的最终块未正确填充

nodeJS之crypto加密

nodeJS之crypto加密

nodeJS之crypto模块md5和Hmac加密

前端DES加密