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 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', 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 };
@shaikh-shahid
Copy link

shaikh-shahid 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/

@drscottlobo
Copy link

drscottlobo commented May 25, 2019

Thanks! This was really helpful!

@cullylarson
Copy link

cullylarson commented Jun 10, 2019

FYI, Must be 256 bytes (32 characters) is incorrect. It's 256 bits (32 characters, 8 bits per character, is 256 bits).

@andrhamm
Copy link

andrhamm commented Jun 11, 2019

256 bits = 32 bytes so crypto.randomBytes(32) can be used to generate ENCRYPTION_KEY (probably want to base64 encode that if you are putting it in the ENV var

@vlucas
Copy link
Author

vlucas commented Jun 14, 2019

Updated main gist and corresponding blog post to use Buffer.from() instead of new Buffer() to be up to date with the latest node standards.

@camsjams
Copy link

camsjams commented Jun 19, 2019

Thanks Vance, was useful for reference for upgrading a Node service I had, previous was supplying a unique salt for createCipher but this is more streamlined.

I had done a browser version of this using the SubtleCrypto api, for anyone interested: https://github.com/camsjams/mr-roboto

@music2code
Copy link

music2code commented Jul 6, 2019

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

Hi great gist! I'm getting two errors -

1st: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt error if I don't set the decipher to decipher.setAutoPadding(false).

2nd: If I do set it to fix the padding to false .. The decrypted text returned is along the lines of: �ㆠT��2ν��үf$}�b����� y\���Ɇ�2�Rd��_�q �o���֥�"�5T�.... Which of course is false when comparing original text before encryption to decrypted text.

Any ideas?? Why this is decrypting this way?

�ㆠT��2ν��үf$}�b����� y\���Ɇ�2�Rd��_�q �o���֥�"�5T�
��1�J���/���W5���e)�m�V�o�ג!��VVl�E��ϰ/@���ܴ^\��}�Z�Ű2C���v�T
�Y��[wx'��6Yw�%���R�^Sǧh+42�Km�#�t����et���C-(� . �B��D�!_���e�0,�-fzO�Z*�ym:h��\d4����b��F�/� �d�����s �%p&l@l�[ǎ���%u��Nڴ�e��u���_�-�_�C�����~��m,���<�(��A�r�6
�a��g@ �,�5Ik��d�������.I��
������|9
��N{��B�sW
�(�Q���MKAE��_T�H��nR}!�g�
�rA�������,J��|�:�c?*�d�b��WJ6��N!S�'��-l

@RizkyArifNur
Copy link

RizkyArifNur commented Jul 8, 2019

@vlucas very useful article, thanks for sharing
i've created a npm package that adopting this concept, and i've added some feature

  1. custom separator
  2. custom encoding (support for base64 and hex)
  3. typescript support

called strong-cryptor you can check it here
github link : https://github.com/RizkyArifNur/strong-cryptor
npm link : https://www.npmjs.com/package/strong-cryptor

@pranaysonisoft
Copy link

pranaysonisoft commented Jul 26, 2019

how to Encryption in Node js and Decryption in browser (client Side) ?

@cullylarson
Copy link

cullylarson commented Jul 26, 2019

@pranaysonisoft FYI, this method uses symmetric encryption. That means the key used to encrypt is the same key used to decrypt. In order to decrypt in the browser, you would need to send the client the key. If you can send the key securely, then there's no point in symmetrically encrypting the data; just send the data using the secure method you used to send the key. If you can't send it securely, then there is no point in encrypting the data.

@trulymittal
Copy link

trulymittal commented Nov 5, 2019

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

You can use ECDH to generate a secret key, if you want end-to-end encryption.

@vidanov
Copy link

vidanov commented Nov 6, 2019

Thank you for the solution! I created an npm package to make it easier to use:
https://www.npmjs.com/package/crypto-simple

@inspiretk
Copy link

inspiretk commented Feb 9, 2020

Thanks @vlucas - If anyone having trouble using code, I did the following.

npm install crypto

Get your 32 character passkey, you can type it yourself or go here and generate it > make sure you change Password length to 32
https://passwordsgenerator.net/

Copy paste that 32 character string into your .env file

In your .env file, put

ENCRYPTION_KEY = 'paste your 32 character string here'

Now Cut and paste all code from above into a js file eg file.js

In the file you want to use the encrypt and decrypt, use import the code like as follows

const { decrypt, encrypt } = require('./file') // path to your code that was cut and paste

and to use it test it out

let data = encrypt('hello')
console.log(data) // you will see the encrypted string
let dataD = decrypt(data)
console.log(dataD) // you will see the decrypted string 'hello'

@meynguy
Copy link

meynguy commented Apr 10, 2020

@vlucas Is it possible to encrypt or decrypt without using the decipher.final() ? as I'm not able to get an encryption or decryption to work if I remove it.

I know this is not advisable. as we don't want the key to stay in memory, but I just want to know, for the sake of my hello world application, that I'm writing to understand the crypto library. So please save me the lecture :)

@kstankov
Copy link

kstankov commented Jan 11, 2021

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

Well, you need to have it in your environment. Either as OS variable or with .env file.
https://www.twilio.com/blog/working-with-environment-variables-in-node-js-html

@einnar82
Copy link

einnar82 commented Mar 29, 2021

Hello,

I have my updated PHP script on @vlucas encryption and decryption using Node.js.

https://gist.github.com/einnar82/3ffbc6e0e3894faa97736c0448d4ed30

Thanks @vlucas, @pablomagro and @Tiriel for your answers.

@nouman0557
Copy link

nouman0557 commented Jun 7, 2021

@vlucas Hi sir ,
Can you please guide me on converting the same code into a typescript code for my angular project. ?

@vlucas
Copy link
Author

vlucas commented Jun 7, 2021

@nouman0557 You're in luck - I actually just recently updated this library to a TypeScript version in my latest project:

import crypto from 'crypto';

const ENCRYPTION_KEY: string = process.env.SC_ENCRYPTION_KEY || ''; // Must be 256 bits (32 characters)
const IV_LENGTH: number = 16; // For AES, this is always 16

export function encrypt(text: string, encryptionKey: string = ENCRYPTION_KEY): string {
  let iv = Buffer.from(crypto.randomBytes(IV_LENGTH)).toString('hex').slice(0, IV_LENGTH);
  let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(encryptionKey), iv);
  let encrypted = cipher.update(text);

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

export function decrypt(text: string, encryptionKey: string = ENCRYPTION_KEY): string {
  let textParts: string[] = text.includes(':') ? text.split(':') : [];
  let iv = Buffer.from(textParts.shift() || '', 'binary');
  let encryptedText = Buffer.from(textParts.join(':'), 'hex');
  let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(encryptionKey), iv);
  let decrypted = decipher.update(encryptedText);

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

@nouman0557
Copy link

nouman0557 commented Jun 9, 2021

@vlucas Thanks for your reply.

Sir, I can't access nodejs crypto module in a web angular application. Node.js uses the same javascript syntax but is not running in the browser.

On the other hand, I have to use the same approach to getting the exact result of encryption and decryption in Angular. As it's confirmed that crypto is not working in Angular. I would like to know any relative way to achieve the required result.
Please, guide me if you have relative knowledge.

@nouman0557
Copy link

nouman0557 commented Jun 9, 2021

image

@vlucas
Copy link
Author

vlucas commented Jun 9, 2021

@nouman0557 This is not a support forum. I cannot help fix your specific issues. This encryption library is specifically for Node.js, not the web, and it even says so in the title.

@camsjams
Copy link

camsjams commented Jun 10, 2021

@nouman0557 up higher in this Gist I pasted a link to my GitHub repo which is a browser version of secret encryption using the native API SubtleCrypto

@nouman0557
Copy link

nouman0557 commented Jun 11, 2021

@camsjams Thank you

@hariprakashnagar
Copy link

hariprakashnagar commented Jul 28, 2021

Above code generating different password every time for same text, how can i validate password is correct when user login

@vlucas
Copy link
Author

vlucas commented Jul 28, 2021

@hariprakashnagar Encryption is not for password validation. It's for password storage, i.e. in a database. If you need to compare, you just pull the user record from the database by email or username, decrypt the password and check it against their input.

@DominusKelvin
Copy link

DominusKelvin commented Aug 9, 2021

For some reasons my encryption key though its a 32 character string keeps getting rejected as a key with invalid key length

@vallyian
Copy link

vallyian commented Nov 16, 2021

@vlucas

@hariprakashnagar Encryption is not for password validation. It's for password storage, i.e. in a database. If you need to compare, you just pull the user record from the database by email or username, decrypt the password and check it against their input.

Never do this, passwords must be stored hashed (not reversible). They are verified by verifying the stored hash against the new hash generated from user input.

@fuxiaoguo
Copy link

fuxiaoguo commented Feb 22, 2022

1 char=8 bits; so 256 bits equal 32 chars

@anarnoli
Copy link

anarnoli commented May 2, 2022

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

Hi great gist! I'm getting two errors -

1st: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt error if I don't set the decipher to decipher.setAutoPadding(false).

2nd: If I do set it to fix the padding to false .. The decrypted text returned is along the lines of: �ㆠT��2ν��үf$}�b����� y\���Ɇ�2�Rd��_�q �o���֥�"�5T�.... Which of course is false when comparing original text before encryption to decrypted text.

Any ideas?? Why this is decrypting this way?

�ㆠT��2ν��үf$}�b����� y\���Ɇ�2�Rd��_�q �o���֥�"�5T� ��1�J���/���W5���e)�m�V�o�ג!��VVl�E��ϰ/@���ܴ^\��}�Z�Ű2C���v�T �Y��[wx'��6Yw�%���R�^Sǧh+42�Km�#�t����et���C-(� . �B��D�!_���e�0,�-fzO�Z*�ym:h��\d4����b��F�/� �d�����s �%p&l@l�[ǎ���%u��Nڴ�e��u���_�-�_�C�����~��m,���<�(��A�r�6 �a��g@ �,�5Ik��d�������.I�� ������|9 ��N{��B�sW�(�Q���MKAE��_T�H��nR}!�g� �rA�������,J��|�:�c?*�d�b��WJ6��N!S�'��-l

@music2code Any luck on your problem? I am in same situation.

@hostrings
Copy link

hostrings commented Aug 20, 2022

Thanks @vlucas - If anyone having trouble using code, I did the following.

npm install crypto

Get your 32 character passkey, you can type it yourself or go here and generate it > make sure you change Password length to 32 https://passwordsgenerator.net/

Copy paste that 32 character string into your .env file

In your .env file, put

ENCRYPTION_KEY = 'paste your 32 character string here'

Now Cut and paste all code from above into a js file eg file.js

In the file you want to use the encrypt and decrypt, use import the code like as follows

const { decrypt, encrypt } = require('./file') // path to your code that was cut and paste

and to use it test it out

let data = encrypt('hello')
console.log(data) // you will see the encrypted string
let dataD = decrypt(data)
console.log(dataD) // you will see the decrypted string 'hello'

Link Updated Password Generator: http://passwordsgenerators.net/

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