12

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

meager
  • 209,754
  • 38
  • 307
  • 315
FredyC
  • 3,339
  • 2
  • 26
  • 36
  • Presumably the key value was created specially for this question, because it's length (25 bytes) is *too short* to be acceptable as an AES-256 key, which must be 32 bytes long. It's also 1 byte too long for a AES-192, which uses a 24-byte key. – Martijn Pieters Jan 21 '20 at 12:17

1 Answers1

21

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?

Tomalak
  • 306,836
  • 62
  • 485
  • 598
  • 1
    If the error message is exactly the same I tend to get suspicious no matter how old the post is. Side note: [How to choose an AES encryption mode (CBC ECB CTR OCB CFB)?](http://stackoverflow.com/questions/1220751/how-to-choose-an-aes-encryption-mode-cbc-ecb-ctr-ocb-cfb) – Tomalak Dec 08 '16 at 16:57
  • This would not work if openssl version on the encryption/decryption machines are different. Is there a possibility to specify the digest algorithm? – Naz Jun 05 '19 at 12:56
  • That's basically a separate question, this should not be tucked away in the comments to a question with a different title. – Tomalak Jun 05 '19 at 18:09
  • 2
    Demo of the updated code: https://runkit.com/mjpieters/bidirectional-encryption-with-nodejs. I do know crypto, but usually from a Python perspective. The use of an IV is exactly right, see [Encrypting using AES 256, do I need IV?](//security.stackexchange.com/q/35210). The IV protects you from leaking information if your first 16 bytes of plaintext were to be repeat across multiple encrypted plaintexts. – Martijn Pieters Jan 21 '20 at 12:37
  • Using this exact code example results in an invalid key length error. Refer https://stackoverflow.com/questions/50963160/invalid-key-length-in-crypto-createcipheriv for solution. – VaibhavJoshi Feb 18 '21 at 06:52
  • @VaibhavJoshi You're right. I'm pretty sure there was no error at the time of writing this, but I can't remember anymore. Answer updated, also using a Buffer instead of a string - no errors are thrown now. – Tomalak Feb 18 '21 at 12:58