I want to decrypt something with the decipher that is part of node.js but I get an error message. If I use forge on the same input data, then the data can be decrypted. What am I doing wrong in the nodeDecrypt function?
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());
Output
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
This is with nodes.js version 14.15.0
CBC mode generally (including this case) needs padding, and your encryption uses padding where only the last byte specifies the length and the other bytes are random. node-forge evidently accepts this, but nodejs builtin crypto
uses OpenSSL (specifically the EVP_Decrypt*
API, as you can see in the error message) which by default implements PKCS5/7 padding where all bytes must contain the length.
OpenSSL, and crypto
, does have an option to not do any padding and unpadding at all, leaving you to do it yourself. You can use that option here as follows:
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());