Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Stronger Encryption and Decryption in Node.js
'use strict';
const crypto = require('crypto');
const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY; // Must be 256 bytes (32 characters)
const IV_LENGTH = 16; // For AES, this is always 16
function encrypt(text) {
let iv = crypto.randomBytes(IV_LENGTH);
let cipher = crypto.createCipheriv('aes-256-cbc', new Buffer(ENCRYPTION_KEY), iv);
let encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
return iv.toString('hex') + ':' + encrypted.toString('hex');
}
function decrypt(text) {
let textParts = text.split(':');
let iv = new Buffer(textParts.shift(), 'hex');
let encryptedText = new Buffer(textParts.join(':'), 'hex');
let decipher = crypto.createDecipheriv('aes-256-cbc', new Buffer(ENCRYPTION_KEY), iv);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
}
module.exports = { decrypt, encrypt };
@vlucas

This comment has been minimized.

Copy link
Owner Author

vlucas commented Jan 27, 2017

@empitegayan

This comment has been minimized.

Copy link

empitegayan commented Mar 15, 2017

Must be 256 bytes (32 characters) how to generate this string ?

@reyhansofian

This comment has been minimized.

Copy link

reyhansofian commented May 17, 2017

you can try to use randomstring to generate 32 characters string

@andreash92

This comment has been minimized.

Copy link

andreash92 commented Jun 27, 2017

I have a more general question.
because of random iv i guess i should store it somewhere safe so i can decrypt my text, right?
Let's say, i store encrypted tokens in a DaaS (i.e. mongodb Atlas or mongoLab). Where do you think i should store every token's iv value?
I guess that is kinda pointless to store them in the same database

@MahdiPishguy

This comment has been minimized.

Copy link

MahdiPishguy commented Jun 28, 2017

i have a question: how can i encrypt string inside android and can be decrypt that with your samples?

@ooip

This comment has been minimized.

Copy link

ooip commented Jul 1, 2017

@vlucas could this be used for streams? Would it be possible to provide an example here? Thanks

@motss

This comment has been minimized.

Copy link

motss commented Jul 17, 2017

@vlucas Not planning to create a NPM package with some test with this amazing Gist?

@vlucas

This comment has been minimized.

Copy link
Owner Author

vlucas commented Aug 7, 2017

@andreash92 You could certainly generate your own iv, and then pass it to this function (you would have to modify it to accept the iv as a second argument). This set of functions was intended to be as simple as possible though, so it stores the iv along with the encrypted text in a single database field. It achieves the desired randomness at rest, and does not add any complexities that people have to worry about other than a single encryption key.

@vlucas

This comment has been minimized.

Copy link
Owner Author

vlucas commented Aug 7, 2017

@MahdiPishguy I really don't know the first thing about Android development, so I have no idea.

@vlucas

This comment has been minimized.

Copy link
Owner Author

vlucas commented Aug 7, 2017

@ooip I am sure that streams could be used instead of buffers - just drop the toString calls and you're already halfway there.

@vlucas

This comment has been minimized.

Copy link
Owner Author

vlucas commented Aug 7, 2017

@motss Not at the moment, no - but I did think about it before making this gist the and the accompanying blog post.

@Mankee

This comment has been minimized.

Copy link

Mankee commented Aug 10, 2017

@vlucas Is it necessary to keep the IV secret / separate from the encrypted data? Or is storing the IV with the encrypted data okay, assuming the encrypted data is accessible to the public.

@Tiriel

This comment has been minimized.

Copy link

Tiriel commented Aug 16, 2017

@Mankee Since the function returns a concat comprising "iv:encrypted". If you prefer, it seems the final ciphered string is built a bit like a bcrypt password: salt+hash.

Or at least it's what I understand. I'm mostly a noob in crypto
@vlucas This is great job. Do you see any way to cipher some data on one server running on PHP then deciphering it in Node using your method?
That is, if I use mcrypt functions with the Rijndael-128 algo, proper 32chars key, CBC mode and randomly generated iv, is there anything opposing decryption by this method?

@Tiriel

This comment has been minimized.

Copy link

Tiriel commented Aug 16, 2017

For those interested, it is indeed possible.
This code is adapted from the Paragon Initiative:

define('AES_METHOD', 'aes-256-cbc');

function encrypt($message, $password)
{
    // Check versions with Heartbleed vulnerabilities
    if (OPENSSL_VERSION_NUMBER <= 268443727) {
        throw new RuntimeException('OpenSSL Version too old');
    }
    
    $iv_size        = openssl_cipher_iv_length(AES_METHOD);
    $iv             = openssl_random_pseudo_bytes($iv_size);
    $ciphertext     = openssl_encrypt($message, AES_METHOD, $password, OPENSSL_RAW_DATA, $iv);
    $ciphertext_hex = bin2hex($ciphertext);
    $iv_hex         = bin2hex($iv);

    return "$iv_hex:$ciphertext_hex";
}

function decrypt($ciphered, $password) {
    $iv_size    = openssl_cipher_iv_length(AES_METHOD);
    $iv         = mb_substr($ciphered, 0, $iv_size, '8bit');
    $ciphertext = mb_substr($ciphered, $iv_size+1, strlen($ciphered), '8bit');

    return openssl_decrypt($ciphertext, AES_METHOD, $password, OPENSSL_RAW_DATA, $iv);
}

echo encrypt($message, $key);

The result of encrypt() can be decrypted by @vlucas 's code, so I guess the reverse is true. Still have to test it though.

@pablomagro

This comment has been minimized.

Copy link

pablomagro commented Sep 5, 2017

@vlucas Thanks for sharing!

If someone is interested in use the function decrypt posted by @Tiriel, just change below code:

$iv = mb_substr($ciphered, 0, $iv_size, '8bit');
$ciphertext = mb_substr($ciphered, $iv_size+1, strlen($ciphered), '8bit');

by this:

$parts = explode(':', $ciphered);
$iv = hex2bin($parts[0]);
$ciphertext = hex2bin($parts[1]);

hex2bin is available with PHP version >= 5.4.0, in php 5.3 you can use pack instead: pack("H*" , $parts[0]);

@Jehanramadhan

This comment has been minimized.

Copy link

Jehanramadhan commented Sep 14, 2017

i got this error

crypto.js:265
this._handle.initiv(cipher, toBuf(key), toBuf(iv));
^

Error: Invalid IV length
at new Decipheriv (crypto.js:265:16)
at Object.Decipheriv (crypto.js:262:12)
at Object.decrypt (/var/www/html/server/lib/hash.js:20:25)
at /var/www/html/server/routes/auth.js:26:24
at newArguments.(anonymous function) (/var/www/html/server/node_modules/nedb/lib/executor.js:29:17)
at Cursor.execFn (/var/www/html/server/node_modules/nedb/lib/datastore.js:518:14)
at callback (/var/www/html/server/node_modules/nedb/lib/cursor.js:126:19)
at /var/www/html/server/node_modules/nedb/lib/cursor.js:193:12
at /var/www/html/server/node_modules/nedb/lib/datastore.js:329:14
at Object.async.eachSeries (/var/www/html/server/node_modules/nedb/node_modules/async/lib/async.js:130:20)

@aksel

This comment has been minimized.

Copy link

aksel commented Sep 27, 2017

Something to note, AES-256 keys are 256 bits, not 256 bytes as the comment states.

@fdelbos

This comment has been minimized.

Copy link

fdelbos commented Oct 20, 2017

@vlucas why encoding the result in hexadecimal instead of base64 ? Also according to the documentation of the cipher.update function : "If the inputEncoding argument is not given, data must be a Buffer, TypedArray, or DataView"

@Globik

This comment has been minimized.

Copy link

Globik commented Nov 28, 2017

What's this? Shouldn't I to store the iv in a database?

@dtrolle

This comment has been minimized.

Copy link

dtrolle commented Dec 7, 2017

When storing data into MySQL database using this function, how can I decrypt my data directly using MYSQL ?
(I mean without using decrypt function). Id' like to be able to fetch some data directly fro database without node, only in MYSQL.)
Thanks

@cggaurav

This comment has been minimized.

Copy link

cggaurav commented Mar 4, 2018

This is good!

@makmayank

This comment has been minimized.

Copy link

makmayank commented Apr 16, 2018

I wanna encrypt files ! how to pass files as parameter . Please help me on running this code for file encryption !

@dkoloditch

This comment has been minimized.

Copy link

dkoloditch commented May 22, 2018

In case anyone runs into Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt, add decipher.setAutoPadding(false) after crypto.createDecipheriv....

Reference here.

@petervavro

This comment has been minimized.

Copy link

petervavro commented Jul 8, 2018

new Buffer( replaced for Buffer.from

`'use strict';

const crypto = require('crypto');

const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY; // Must be 256 bytes (32 characters)
const IV_LENGTH = 16; // For AES, this is always 16

function encrypt(text) {
let iv = crypto.randomBytes(IV_LENGTH);
let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(ENCRYPTION_KEY), iv);
let encrypted = cipher.update(text);

encrypted = Buffer.concat([encrypted, cipher.final()]);

return iv.toString('hex') + ':' + encrypted.toString('hex');
}

function decrypt(text) {
let textParts = text.split(':');
let iv = Buffer.from(textParts.shift(), 'hex');
let encryptedText = Buffer.from(textParts.join(':'), 'hex');
let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(ENCRYPTION_KEY), iv);
let decrypted = decipher.update(encryptedText);

decrypted = Buffer.concat([decrypted, decipher.final()]);

return decrypted.toString();
}

module.exports = { decrypt, encrypt };`

@paulmowat

This comment has been minimized.

Copy link

paulmowat commented Jul 13, 2018

Thanks very much for this. Really helped me out.

I've created a C# version that will work in conjunction with the node.js one to encrypt/decrypt.

Can be found at https://github.com/paulmowat/EncryptDecrypt for anyone who needs it :)

@swapnilmarathe007

This comment has been minimized.

Copy link

swapnilmarathe007 commented Aug 27, 2018

Key point to note, AES-256 keys are 256 bits, not 256 bytes.

@neeraj87

This comment has been minimized.

Copy link

neeraj87 commented Sep 9, 2018

The Buffer() and new Buffer() constructors are not recommended for use due to security and usability concerns. Please use the new Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() construction methods instead.

Updated Code:

const crypto = require('crypto');
const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY; // Must be 256 bits (32 characters)
const IV_LENGTH = 16; // For AES, this is always 16

function encrypt(text) {
	let iv = crypto.randomBytes(IV_LENGTH);
	let cipher = crypto.createCipheriv('aes-256-cbc', new Buffer.from(ENCRYPTION_KEY), iv);
	let encrypted = cipher.update(text);

	encrypted = Buffer.concat([encrypted, cipher.final()]);
	return iv.toString('hex') + ':' + encrypted.toString('hex');
}

function decrypt(text) {
	let textParts = text.split(':');
	let iv = new Buffer.from(textParts.shift(), 'hex');
	let encryptedText = new Buffer.from(textParts.join(':'), 'hex');
	let decipher = crypto.createDecipheriv('aes-256-cbc', new Buffer.from(ENCRYPTION_KEY), iv);
	let decrypted = decipher.update(encryptedText);

	decrypted = Buffer.concat([decrypted, decipher.final()]);
	return decrypted.toString();
}

module.exports = { decrypt, encrypt };

More information on this link: https://nodejs.org/en/docs/guides/buffer-constructor-deprecation/

@vishwajeetsonkar

This comment has been minimized.

Copy link

vishwajeetsonkar commented Oct 11, 2018

@neeraj87 your above code really helped me but i am getting an error like this

Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
at Decipheriv.final (crypto.js:183:26)
at decryption (/Users/pankajpbaviskar/Desktop/Vishwajeet/Teams/twentyfirstsuperadmin/server/middleware/crypto.js:36:42)

below is my code to decrypt
let textParts = res.locals[0][key].split(':');
let iv = new Buffer.from(textParts[0], 'hex');
let encryptedText = new Buffer.from(textParts[1], 'hex');
let decipher = crypto.createDecipheriv('aes-256-cbc', new Buffer.from(KEY), iv);
let decrypted = decipher.update(encryptedText);
console.log(decrypted, 'first');
decrypted = Buffer.concat([decrypted, decipher.final('utf8')]);
console.log(decrypted.toString());

I think i am getting error for decipher.final() but don't know how to fix it.

@krishana-s

This comment has been minimized.

Copy link

krishana-s commented Nov 29, 2018

process.env.ENCRYPTION_KEY is coming undefined.how ENCRYPTION_KEY will generate?

@alucard001

This comment has been minimized.

Copy link

alucard001 commented Dec 5, 2018

process.env.ENCRYPTION_KEY is coming undefined.how ENCRYPTION_KEY will generate?

It uses dotenv to load. You need to install dotenv and in your .env file, you need to have something like:

# Must be 32 Chars
ENCRYPTION_KEY=my_32_chars_encryption_key_12345
@Bilen2

This comment has been minimized.

Copy link

Bilen2 commented Dec 17, 2018

im getting the following error when using the updated code provided by : @neeraj87
TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be one of type string, Buffer, TypedArray, or DataView. Received type object

and im using a 32 character random string for my password.

@krasimir

This comment has been minimized.

Copy link

krasimir commented Dec 27, 2018

im getting the following error when using the updated code provided by : @neeraj87
TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be one of type string, Buffer, TypedArray, or DataView. Received type object

and im using a 32 character random string for my password.

Try JSON.stringify(text) when you pass it to the encrypt function.

@jm555jm

This comment has been minimized.

Copy link

jm555jm commented Dec 29, 2018

error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
I solved this issue by changing plain text to burrfer first and concat the encrypted with cipher.final()
I guess the reason of this issue is invalid padding.

  let iv = crypto.randomBytes(IV_LENGTH)
  let key = new Buffer.from(ENCRYPTION_KEY)
  let plain = new Buffer.from(text)
  let cipher = crypto.createCipheriv('aes-256-cbc', key, iv)
  let encrypted = cipher.update(plain)
  encrypted = Buffer.concat([encrypted, cipher.final()])
  return iv.toString('hex') + ':' + encrypted.toString('hex')
@vlucas

This comment has been minimized.

Copy link
Owner Author

vlucas commented Jan 3, 2019

Thanks for the updated code, @neeraj87

@AthenasEngineering

This comment has been minimized.

Copy link

AthenasEngineering commented Jan 25, 2019

@andreash92

I have a more general question.
because of random iv i guess i should store it somewhere safe so i can decrypt my text, right?
Let's say, i store encrypted tokens in a DaaS (i.e. mongodb Atlas or mongoLab). Where do you think i should store every token's iv value?
I guess that is kinda pointless to store them in the same database

Late to the party, but oh well. The IV is not as big of a secret as the actual secret is. They're supposed to be unpredictable and preferably cryptographically random. From the Node.js docs:

Initialization vectors should be unpredictable and unique; ideally, they will be cryptographically random. They do not have to be secret: IVs are typically just added to ciphertext messages unencrypted. It may sound contradictory that something has to be unpredictable and unique, but does not have to be secret; it is important to remember that an attacker must not be able to predict ahead of time what a given IV will be.

@a4amaan

This comment has been minimized.

Copy link

a4amaan commented Mar 19, 2019

I'm looking for encryption and decryption alternative for This Python, C#, PHP in NodeJs. Thanks in Advance.

@codeforgeek

This comment has been minimized.

Copy link

codeforgeek commented Mar 28, 2019

Have a look at this post for complete reference:

https://codeforgeek.com/2018/10/encrypt-and-decrypt-data-in-node-js/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.