Create a gist now

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.

Show comment
Hide comment
@empitegayan

This comment has been minimized.

Show comment
Hide comment
@empitegayan

empitegayan Mar 15, 2017

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

empitegayan commented Mar 15, 2017

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

@reyhansofian

This comment has been minimized.

Show comment
Hide comment
@reyhansofian

reyhansofian May 17, 2017

you can try to use randomstring to generate 32 characters string

reyhansofian commented May 17, 2017

you can try to use randomstring to generate 32 characters string

@andreash92

This comment has been minimized.

Show comment
Hide comment
@andreash92

andreash92 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

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.

Show comment
Hide comment
@MahdiPishguy

MahdiPishguy Jun 28, 2017

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

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.

Show comment
Hide comment
@ooip

ooip Jul 1, 2017

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

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.

Show comment
Hide comment
@motss

motss Jul 17, 2017

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

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.

Show comment
Hide comment
@vlucas

vlucas 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.

Owner

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.

Show comment
Hide comment
@vlucas

vlucas Aug 7, 2017

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

Owner

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.

Show comment
Hide comment
@vlucas

vlucas 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.

Owner

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.

Show comment
Hide comment
@vlucas

vlucas 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.

Owner

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.

Show comment
Hide comment
@Mankee

Mankee 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.

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.

Show comment
Hide comment
@Tiriel

Tiriel 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 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.

Show comment
Hide comment
@Tiriel

Tiriel 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.

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.

Show comment
Hide comment
@pablomagro

pablomagro 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]);

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.

Show comment
Hide comment
@Jehanramadhan

Jehanramadhan 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)

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.

Show comment
Hide comment
@aksel

aksel Sep 27, 2017

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

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.

Show comment
Hide comment
@fdelbos

fdelbos 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"

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.

Show comment
Hide comment
@Globik

Globik Nov 28, 2017

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

Globik commented Nov 28, 2017

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

@dtrolle

This comment has been minimized.

Show comment
Hide comment
@dtrolle

dtrolle 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

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.

Show comment
Hide comment
@cggaurav

cggaurav Mar 4, 2018

This is good!

cggaurav commented Mar 4, 2018

This is good!

@makmayank

This comment has been minimized.

Show comment
Hide comment
@makmayank

makmayank Apr 16, 2018

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

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.

Show comment
Hide comment
@dkoloditch

dkoloditch 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.

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.

Show comment
Hide comment
@petervavro

petervavro 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 };`

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.

Show comment
Hide comment
@paulmowat

paulmowat 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 :)

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 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment