-
-
Save vlucas/2bd40f62d20c1d49237a109d491974eb to your computer and use it in GitHub Desktop.
'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 }; |
@vlucas very useful article, thanks for sharing
i've created a npm package that adopting this concept, and i've added some feature
- custom separator
- custom encoding (support for
base64
andhex
) - 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
how to Encryption in Node js and Decryption in browser (client Side) ?
@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.
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.
Thank you for the solution! I created an npm package to make it easier to use:
https://www.npmjs.com/package/crypto-simple
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'
@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 :)
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
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.
@vlucas Hi sir ,
Can you please guide me on converting the same code into a typescript code for my angular project. ?
@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();
}
@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 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.
@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
@camsjams Thank you
Above code generating different password every time for same text, how can i validate password is correct when user login
@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.
For some reasons my encryption key though its a 32 character string keeps getting rejected as a key with invalid key length
@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.
1 char=8 bits; so 256 bits equal 32 chars
@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�K�(�Q���MKAE��_T�H��nR}!�g� �rA�������,J��|�:�c?*�d�b��WJ6��N!S�'��-lm�#�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
@music2code Any luck on your problem? I am in same situation.
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/
The password generator that you included doesn't load anymore. You can try this one.
https://passwords-generator.org
I am unable to run the code.....whenever I am entering the string.....it gives me an error...
made this package a while ago for server side encrypted payloads to use for backend only: https://www.npmjs.com/package/encryption.js
I'm surprised by the glaring mistakes (?) in how the encryption key is actually 16 bytes and IV is 8 bytes?
This is largely because he's reading a string as a buffer, without using the hex
argument.
Fixes
- Because he's reading the hex strings without the
hex
argument, it's parsed incorrectly. All hex values are read half their size - so 32 bytes is actually 16 bytes and 16 bytes is 8 bytes. Amending this also fixes theInvalid key length
errors that people have in the thread.
// Previously
Buffer.from(encryptionKey)
// Now
Buffer.from(encryptionKey, 'hex')
- Take note of the line, there's absolutely no reason as why you should need to
.slice
your string if it's 16 bytes. If you generate a string of 16 bytes, that should fit fully into thecreateCipheriv
function from Node'scrypto
package.
let iv = Buffer.from(crypto.randomBytes(IV_LENGTH)).toString('hex').slice(0, IV_LENGTH);
- Added
keyGen()
function that generates a key for you, so that there's no misunderstanding or ambiguity in how you should generate this key. Saw some people mention other random string generation packages, but there's no reason to have that dependency when you can usecrypto
'srandomBytes
directly. - Use
const
for variables instead oflet
.
Contribute
There are ways to improve the code futher. If you want to improve this, then you could:
- Add better error messages for e.g. when cipher formats are incorrect
- Read and write hex with the prefix
0x
for less ambiguity - Better TypeScript types for Hex values, etc.
I'll leave that to someone else to figure out although, if someone wants to.
Updated code (2024-02-15)
You should use this instead of OP and OP's TypeScript version posted above.
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 keyGen() {
return crypto.randomBytes(32).toString('hex');
}
export function encrypt(plainText: string, keyHex: string = ENCRYPTION_KEY): string {
const iv = crypto.randomBytes(IV_LENGTH); // Directly use Buffer returned by randomBytes
const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(keyHex, 'hex'), iv);
const encrypted = Buffer.concat([cipher.update(plainText, 'utf8'), cipher.final()]);
// Return iv and encrypted data as hex, combined in one line
return iv.toString('hex') + ':' + encrypted.toString('hex');
}
export function decrypt(text: string, keyHex: string = ENCRYPTION_KEY): string {
const [ivHex, encryptedHex] = text.split(':');
if (!ivHex || !encryptedHex) {
throw new Error('Invalid or corrupted cipher format');
}
const encryptedText = Buffer.from(encryptedHex, 'hex');
const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(keyHex, 'hex'), Buffer.from(ivHex, 'hex'));
let decrypted = Buffer.concat([decipher.update(encryptedText), decipher.final()]);
return decrypted.toString();
}
Great work @nijynot !
Nice work by @nijynot! If anyone is facing "TypeError: Cannot read properties of undefined (reading 'randomBytes')", change the import import crypto from 'crypto';
to import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'
. Wonder why ES gives this error, but this fix should work
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�(�Q���MKAE��_T�H��nR}!�g��Y��[wx'��6Yw�%���R�^Sǧh+42�K
m�#�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
�rA�������,J��|�:�c?*�d�b��WJ6��N!S�'��-l