首页 > 解决方案 > AES-CBC 解密 node.js 与伪造数字信封例程:EVP_DecryptFinal_ex:bad decrypt

问题描述

我想用 node.js 的一部分解密一些东西,但我收到一条错误消息。如果我对相同的输入数据使用伪造,那么数据可以被解密。我在 nodeDecrypt 函数中做错了什么?

const crypto = require('crypto');

const keyHash = "b6db3d66f4f8bd82aea61576e221f23634bb7c585340a8a42140701f5a468e04"
const encryptedB64 = "cHIaTs0vA6phV8jyT3X78cTSrUnLeBwbAqstVBAl7kl4uV+4oGQFVgsChW8lfw4QOyECkZAay7c0rDi816T9ZA==";

const encryptedBuffer = Buffer.from(encryptedB64, 'base64');

var userKey = Buffer.from(keyHash, 'hex');

const forge = require('node-forge');


function nodeDecrypt(encrypted, key) {
    const iv = encrypted.slice(0, 16);
    const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);

    encrypted = encrypted.slice(16);

    var decrypted = decipher.update(encrypted);

    console.log(decrypted.toString());
    try {
        decrypted += decipher.final();
    } catch (e) {
        console.log(e);
    }
    
    return decrypted;
}

function forgeDecrypt(encrypted, key) {
    const encoding = 'latin1';
    key = key.toString(encoding);
    initVal = encrypted.toString(encoding).substring(0, 16);
    encrypted = encrypted.toString(encoding).substring(16);
    var decipher = forge.cipher.createDecipher('AES-CBC', key);
    decipher.start({ iv: initVal });
    decipher.update(forge.util.createBuffer(encrypted));
    var result = decipher.finish();
    return decipher.output.getBytes();
}
var decrypted;

decrypted = nodeDecrypt(encryptedBuffer, userKey);
console.log(decrypted.toString());

decrypted = forgeDecrypt(encryptedBuffer, userKey);
console.log(decrypted.toString());

输出

1a24989e-75d1-4631-8210-b17bb5e6
decryptor.js:26
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
    at Decipheriv.final (<node_internals>/internal/crypto/cipher.js:174:29)
...
    at internal/main/run_main_module.js:17:47 {library: 'digital envelope routines', function: 'EVP_DecryptFinal_ex', reason: 'bad decrypt', code: 'ERR_OSSL_EVP_BAD_

1a24989e-75d1-4631-8210-b17bb5e6

1a24989e-75d1-4631-8210-b17bb5e6a2a1

这是 node.js 版本 14.15.0

标签: node.jsencryptionaes

解决方案


CBC 模式通常(包括这种情况)需要填充,并且您的加密使用填充,其中只有最后一个字节指定长度,其他字节是随机的。node-forge 显然接受了这一点,但 nodejs 内置crypto使用 OpenSSL(特别是EVP_Decrypt*API,如您在错误消息中所见),默认情况下实现PKCS5/7 填充,其中所有字节都必须包含长度。

OpenSSL 和crypto确实可以选择根本不进行任何填充和取消填充,让您自己做。您可以在此处使用该选项,如下所示:

const crypto = require('crypto');

const keyHash = "b6db3d66f4f8bd82aea61576e221f23634bb7c585340a8a42140701f5a468e04"
const encryptedB64 = "cHIaTs0vA6phV8jyT3X78cTSrUnLeBwbAqstVBAl7kl4uV+4oGQFVgsChW8lfw4QOyECkZAay7c0rDi816T9ZA==";

const encryptedBuffer = Buffer.from(encryptedB64, 'base64');

var userKey = Buffer.from(keyHash, 'hex');

const forge = require('node-forge');


function nodeDecrypt(encrypted, key) {
    const iv = encrypted.slice(0, 16);
    const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
    decipher.setAutoPadding(false);
    encrypted = encrypted.slice(16);
    var decrypted = Buffer.concat([decipher.update(encrypted),decipher.final()]);
    var padlen = decrypted[decrypted.length-1];    
    return decrypted.slice(0,decrypted.length-padlen);
}

function forgeDecrypt(encrypted, key) {
    const encoding = 'latin1';
    key = key.toString(encoding);
    initVal = encrypted.toString(encoding).substring(0, 16);
    encrypted = encrypted.toString(encoding).substring(16);
    var decipher = forge.cipher.createDecipher('AES-CBC', key);
    decipher.start({ iv: initVal });
    decipher.update(forge.util.createBuffer(encrypted));
    var result = decipher.finish();
    return decipher.output.getBytes();
}
var decrypted;

decrypted = nodeDecrypt(encryptedBuffer, userKey);
console.log(decrypted.toString());

decrypted = forgeDecrypt(encryptedBuffer, userKey);
console.log(decrypted.toString());

推荐阅读