Reputation: 4067
I am totally confused why this isn't working, I am getting Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
var crypto = require('crypto');
var key = "ciw7p02f70000ysjon7gztjn7";
var pt = "72721827b4b4ee493ac09c635827c15ce014c3c3";
var encrypt = crypto.createCipher('aes256', key);
encrypt.update(pt, 'utf8', 'hex');
var encrypted = encrypt.final('hex')
var decrypt = crypto.createDecipher('aes256', key);
decrypt.update(encrypted, 'hex', 'utf8')
decrypt.final()
You can see it action using RunKit ... https://runkit.com/fredyc/bidirectional-encryption-with-nodejs
Upvotes: 16
Views: 20382
Reputation: 1540
A modern Web Crypto solution, since this is the first result for node.js symmetric encryption:
const { subtle } = globalThis.crypto;
async function generateAesKey(length = 256) {
const key = await subtle.generateKey(
{
name: "AES-CBC",
length,
},
true,
["encrypt", "decrypt"]
);
return key;
}
async function aesEncrypt(plaintext, key) {
const ec = new TextEncoder();
const iv = crypto.getRandomValues(new Uint8Array(16));
const ciphertext = await crypto.subtle.encrypt(
{
name: "AES-CBC",
iv,
},
key,
ec.encode(plaintext)
);
return {
iv,
ciphertext,
};
}
async function aesDecrypt(ciphertext, key, iv) {
const dec = new TextDecoder();
const plaintext = await crypto.subtle.decrypt(
{
name: "AES-CBC",
iv,
},
key,
ciphertext
);
return dec.decode(plaintext);
}
(async () => {
// generate key
const key = await generateAesKey();
// encrypt
const plaintext = "foo bar";
const { ciphertext, iv } = await aesEncrypt(plaintext, key);
// decrypt
console.log(await aesDecrypt(ciphertext, key, iv));
})();
This has mostly been adapted from https://nodejs.org/api/webcrypto.html
Upvotes: -2
Reputation: 338108
Solution via https://github.com/nodejs/node-v0.x-archive/issues/6386
// https://github.com/nodejs/node-v0.x-archive/issues/6386#issuecomment-31817919
// with createCipher / createDecipher (both deprecated) replaced with
// createCipheriv / createDecipheriv and a generated IV passed along.
var assert = require('assert');
var crypto = require('crypto');
var algorithm = 'aes256';
var inputEncoding = 'utf8';
var outputEncoding = 'hex';
var ivlength = 16 // AES blocksize
var key = Buffer.from('ciw7p02f70000ysjon7gztjn7c2x7GfJ', 'latin1'); // key must be 32 bytes for aes256
var iv = crypto.randomBytes(ivlength);
var text = '72721827b4b4ee493ac09c635827c15ce014c3c3';
console.log('Ciphering "%s" with key "%s" using %s', text, key, algorithm);
var cipher = crypto.createCipheriv(algorithm, key, iv);
var ciphered = cipher.update(text, inputEncoding, outputEncoding);
ciphered += cipher.final(outputEncoding);
var ciphertext = iv.toString(outputEncoding) + ':' + ciphered
console.log('Result in %s is "%s"', outputEncoding, ciphertext);
var components = ciphertext.split(':');
var iv_from_ciphertext = Buffer.from(components.shift(), outputEncoding);
var decipher = crypto.createDecipheriv(algorithm, key, iv_from_ciphertext);
var deciphered = decipher.update(components.join(':'), outputEncoding, inputEncoding);
deciphered += decipher.final(inputEncoding);
console.log(deciphered);
assert.equal(deciphered, text, 'Deciphered text does not match!');
the usage error is here:
// yours (incorrect)
var encrypt = crypto.createCipher('aes256', key);
encrypt.update(pt, 'utf8', 'hex');
var encrypted = encrypt.final('hex')
// correct
var encrypt = crypto.createCipher('aes256', key);
var encrypted = encrypt.update(pt, 'utf8', 'hex');
encrypted += encrypt.final('hex')
// yours (incorrect)
var decrypt = crypto.createDecipher('aes256', key);
decrypt.update(encrypted, 'hex', 'utf8')
decrypt.final()
// correct
var decrypt = crypto.createDecipher('aes256', key);
var decrypted = decrypt.update(encrypted, 'hex', 'utf8')
decrypted += decrypt.final()
but because cipher.createCipher()
and cipher.createDecipher()
are now deprecated and insecure, the solution above uses cipher.createCipheriv()
and cipher.createDecipheriv()
, instead.
The addition of a random IV protects you from leaking information if you encrypt multiple plaintexts that share the first same 16 bytes (or multiple of 16 bytes) at the start of the message. See Encrypting using AES 256, do I need IV?
Upvotes: 30