How to encrypt and decrypt string/object in nodejs - node.js

I would like to encrypt an object then decrypt it. The encryption works very well but the decryption fails. Below my code :
crypto_ext.js
const crypto = require("crypto")
const password = "shared_key"
const algorithm = "aes256"
export const encrypt = (text) => {
if(!text) return ''
const cipher = crypto.createCipher(algorithm, password);
let crypted = cipher.update(text, 'utf-8', 'base64');
crypted += cipher.final('base64');
return crypted;
}
export const decrypt = (text) => {
if(!text) return ''
const decipher = crypto.createDecipher(algorithm, password);
let decrypted = decipher.update(text, 'base64', 'utf-8');
decrypted += decipher.final('utf-8');
return decrypted;
}
and in my test.js, I have :
import {encrypt, decrypt} from './crypto_ext.js'
let test = {key1: val1, key2: val2}
test = encrypt(JSON.stringify(test)) || test
console.log("Encrypt : ", test)
console.log("Decrypt : ", decrypt(test)) // I should have my object as string here
And this is what I'm getting as error :
Uncaught Error: unable to decrypt data
at unpad (decrypter.js:83)
at Decipher.webpackJsonp../node_modules/browserify-aes/decrypter.js.Decipher._final (decrypter.js:38)
at Decipher.webpackJsonp../node_modules/cipher-base/index.js.CipherBase._finalOrDigest (index.js:76)
at decrypt (crypto_ext.js:17)
...
Can you please tell me what I'm doing wrong ?

Method createCipher deprecated. Use createCipheriv instead.
Try to use aes192 algorithm.

Dependencies: crypto package
You can achieve encoding and decoding with below codes:-
const crypto = require('crypto');
var password = 'ojisdasjdsjabdjs';
var iv = 'kiamdksndn';
function sha1(input) {
return crypto.createHash('sha1').update(input).digest();
}
function password_derive_bytes(password, salt, iterations, len) {
var key = Buffer.from(password + salt);
for (var i = 0; i < iterations; i++) {
key = sha1(key);
}
if (key.length < len) {
var hx = password_derive_bytes(password, salt, iterations - 1, 20);
for (var counter = 1; key.length < len; ++counter) {
key = Buffer.concat([key, sha1(Buffer.concat([Buffer.from(counter.toString()), hx]))]);
}
}
return Buffer.alloc(len, key);
}
async function encode(string) {
var key = password_derive_bytes(password, '', 100, 32);
var cipher = crypto.createCipheriv('aes-256-cbc', key, Buffer.from(iv));
var part1 = cipher.update(string, 'utf8');
var part2 = cipher.final();
const encrypted = Buffer.concat([part1, part2]).toString('base64');
return encrypted;
}
async function decode(string) {
var key = password_derive_bytes(password, '', 100, 32);
var decipher = crypto.createDecipheriv('aes-256-cbc', key, Buffer.from(iv));
var decrypted = decipher.update(string, 'base64', 'utf8');
decrypted += decipher.final();
return decrypted;
}
And then call below functions for encode and decode
For encode
await encode(JSON.stringify({'title': 'some text'}));
For decode
await decode('encoded_string_to_decode');

Try to use 'bcrypt' package it will help you in the encryption for passwords.
If you want to encryption for Data. Then use crypto or node-rsa
Link npm bcrypt package
Node-RSA

Related

Implementing DES encryption in NodeJS (Crypto)

I'm trying to convert the below java function for DES encryption into NodeJS.
public static String decrypt (String value) {
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(SALT, COUNT);
PBEKeySpec pbeKeySpec = new PBEKeySpec(KEY);
SecretKeyFactory keyFac = SecretKeyFactory.getInstance(SECRET_KEY);
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
Cipher dcipher = Cipher.getInstance(SECRET_KEY);
dcipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(value);
byte[] utf8 = dcipher.doFinal(dec);
return new String(utf8, UTF8);
}
After some research, I was able to implement the below function in NodeJS.
function decrypt(input) {
const encrypted = Buffer.from(input, 'base64')
const key = crypto.pbkdf2Sync(SECRET_KEY, salt, ITERATION_COUNT, SECRET_KEY.length, 'md5');
const cipher = crypto.createDecipher('des-cbc', key);
const decrypted = Buffer.concat([cipher.update(encrypted, 'base64'), cipher.final()])
return decrypted.toString('utf-8');
}
When I try to decrypt an already encrypted string, I'm getting some garbage value. Also, I don't understand where should I add the KEY value in NodeJS as in java code.
Please help me to understand what I'm doing wrong here.
UPDATE
As mentioned by #Topaco in the comment, the name SECRET_KEY is misleading in the original java code. I found it as PBEWithMD5AndDES and was able to search with the same name and find a code snippet that solves my issue.
The code snippet is available as a gist: https://gist.github.com/qzaidi/5401800
As mentioned by #Topaco in the comment, the name SECRET_KEY is misleading in the original java code. I found it as PBEWithMD5AndDES and was able to search with the same name and find a code snippet that solves my issue.
The code snippet is available as a gist: https://gist.github.com/qzaidi/5401800
"use strict";
/*
* Emulates Java's PBEWITHMD5ANDDES for node.js
*/
var crypto = require('crypto');
var pbewithmd5anddes = {
KDF: function(password, salt, iterations) {
var pwd = new Buffer(password, 'utf-8');
var key = Buffer.concat([pwd, salt]);
var i;
for (i = 0; i < iterations; i += 1) {
key = crypto.createHash("md5").update(key).digest();
}
return key;
},
getKeyIV: function(password, salt, iterations) {
var key = this.KDF(password, salt, iterations);
var keybuf = new Buffer(key, 'binary').slice(0, 8);
var ivbuf = new Buffer(key, 'binary').slice(8, 16);
return [keybuf, ivbuf];
},
encrypt: function(payload, password, salt, iterations, cb) {
var kiv = this.getKeyIV(password, salt, iterations);
var cipher = crypto.createCipheriv('des', kiv[0], kiv[1]);
var encrypted = [];
encrypted.push(cipher.update(payload, 'utf-8', 'hex'));
encrypted.push(cipher.final('hex'));
return cb(undefined, new Buffer(encrypted.join(''), 'hex').toString('base64'));
},
decrypt: function(payload, password, salt, iterations, cb) {
var encryptedBuffer = new Buffer(payload, 'base64');
var kiv = this.getKeyIV(password, salt, iterations);
var decipher = crypto.createDecipheriv('des', kiv[0], kiv[1]);
var decrypted = [];
decrypted.push(decipher.update(encryptedBuffer));
decrypted.push(decipher.final());
return cb(undefined, decrypted.join(''));
}
};
module.exports = pbewithmd5anddes;
/* ---------------- TEST CODE ---------------- */
(function() {
if (require.main === module) {
var password = 'test';
var iterations = 19;
var salt = new Buffer('d99bce325735e303', 'hex');
pbewithmd5anddes.encrypt('helloworld', password, salt, iterations, function(err, msg) {
console.log('encrypted: ' + msg);
// eat your own dogfood
pbewithmd5anddes.decrypt(msg, password, salt, iterations, function(err, msg) {
console.log('decrypted: ' + msg);
});
});
}
}());

AES-256-GCM decryption in nodejs

i am trying to decrypt the data stored in my database before sending it to the client side.
I am using the builtin crypto module with AES-256-GCM encryption.
I have successfully implemented the encryption and it is working properly my problem is i am trying to decrypt the data in a different file but i keep getting an error.
this is the error:
(node:35798) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_ARG_TYPE]: The "iv" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined
1.js
router.post(
"/",
async(req, res) => {
function getFullAddress({housenumber, address1, address2, city, postcode, country}) {
return [housenumber, address1, ...(address2 ? [address2]: []), city, postcode, country].join(", ");
}
const aes256gcm = (key) => {
const encrypt = (str) => {
const iv = new crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
let enc = cipher.update(str, 'utf8', 'base64');
enc += cipher.final('base64');
return Buffer.concat([Buffer.from(enc), iv, cipher.getAuthTag()]).toString("base64");
};
return {
encrypt,
};
};
const aesCipher = aes256gcm(key);
const hashedPasscode = await bcrypt.hash(req.body.passcode, 12);
await User.create({
email: req.body.email,
mobilenumber: aesCipher.encrypt(req.body.mobilenumber),
passcode: hashedPasscode,
address: aesCipher.encrypt(getFullAddress(req.body))
})
2.js
router.get(
"/",
async(req, res) => {
const aes256gcm = (key) => {
const decrypt = (enc, iv, authTag) => {
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(authTag);
let str = decipher.update(enc, 'base64', 'utf8');
str += decipher.final('utf8');
return str;
};
return {
decrypt,
};
};
const aesCipher = aes256gcm(key);
const decrypted_MobileNumber = aesCipher.decrypt(user.mobilenumber);
const decrypted_address = aesCipher.decrypt(user.address);
console.log('decrypted_MobileNumber',decrypted_MobileNumber)
console.log('decrypted_address',decrypted_address)
here is an example of the data stored in my database
mobilenumber: 'Sm4xQjA2bmUwUUdEdW4zQkZ3PT3QEq5fBbTJ9ht4TgpQXTLmPYBSoQA836977j0rr3GYwg==',
This is what you do during encryption:
Buffer.concat([Buffer.from(enc), iv, cipher.getAuthTag()]).toString("base64");
Now, you need to reverse this during decryption:
enc = Buffer.from(enc, "base64");
const iv = enc.slice(enc.length-32, enc.length-16);
const tag = enc.slice(enc.length-16);
enc = enc.slice(0, enc.length-32);
The second issue is that a nonce/iv for GCM mode should be 12 bytes long. I've changed that and so some of the indices from the previous issue should change too.
The third issue is that you cannot concatenate encrypted and Base64-encoded chunks. You have to concatenate them before Base64 encoding so that there is no Base64 padding in the middle of the string. This shouldn't be much of an issue for GCM because the call to cipher.final('base64'); should return an empty string.
The fourth and clear issue is that during encryption you're encoding twice, but you only need to encode once.
And together this would look like this:
const crypto = require('crypto');
const aes256gcm = (key) => {
const encrypt = (str) => {
const iv = new crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
let enc1 = cipher.update(str, 'utf8');
let enc2 = cipher.final();
return Buffer.concat([enc1, enc2, iv, cipher.getAuthTag()]).toString("base64");
};
const decrypt = (enc) => {
enc = Buffer.from(enc, "base64");
const iv = enc.slice(enc.length - 28, enc.length - 16);
const tag = enc.slice(enc.length - 16);
enc = enc.slice(0, enc.length - 28);
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);
let str = decipher.update(enc, null, 'utf8');
str += decipher.final('utf8');
return str;
};
return {
encrypt,
decrypt,
};
};
const cipher = aes256gcm(Buffer.alloc(32)); // just a test key
const ct = cipher.encrypt('test');
const pt = cipher.decrypt(ct);
console.log(pt);

NodeJS crypto for encrypt/decrypt

I'm working on a database with some encrypted data that was generated in ColdFusion using the code below:
key = "nQw7y6QejwGFh/SNrul20Q==";
encrypt(myText, key, "AES/CBC/PKCS5Padding", "HEX");
It generates an encrypted string like: 6F795025756EC54D60808EA98AC163D9143C2FCFEC1065FCCAB7AB0CD577E535. I can decrypt it using my code below
I managed to create my NodeJS class to decrypt this data.
const crypto = require('crypto');
const key = Buffer.from('nQw7y6QejwGFh/SNrul20Q==', 'base64');
module.exports = class Encrypt {
decryptText(text) {
try {
const ivCiphertext = Buffer.from(text, 'hex');
const iv = ivCiphertext.slice(0, 16);
const ciphertext = ivCiphertext.slice(16);
var decipher = crypto.createDecipheriv('AES-128-CBC', key, iv);
var value =
decipher.update(ciphertext, '', 'utf8') +
decipher.final('utf8');
return value;
} catch (err) {
console.log(err);
}
}
};
I'm trying to create a encrypt method in this class, to encrypt the data in the same format that it was being generated in ColdFusion.
encrypt(text) {
const iv = crypto.randomBytes(16);
let cipher = crypto.createCipheriv(
'AES-128-CBC', key, iv);
let encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
return encrypted.toString('hex');
}
I did the following test:
const encrypt = new Encrypt();
const test = encrypt.encrypt('803315808');
console.log(test);
console.log(encrypt.decryptText(test));
First log:
fdcec1c7098c0fc91a11ada1e849b543
Second log:
Error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length

encrypt data using aes-128-ecb in nodejs

I have To encrypt data using aes-128-ecb in nodejs my code is
I am using Crypto to encrypt data
const crypto = require('crypto');
const secret = '1000060000000000';
const cipher = crypto.createCipher('aes-128-ecb', secret);
const ciphertext = cipher.update('9', 'utf8', 'base64')+cipher.final('base64');
console.log("Cipher text is: " + ciphertext);
the output should be EtgITaHs6lEvEHBipj08Kg==
but the output is coming as nNzqejauQBnfiDqznGhZ0Q==
The problem here is the use of crypto.createCipher, it doesn't use the key directly, but rather a digest.
To quote the documentation:
The implementation of crypto.createCipher() derives keys using the OpenSSL function EVP_BytesToKey with the digest algorithm set to MD5, one iteration, and no salt.
If we use cipher.createCipheriv on the other hand, we can specify the key directly and it will give us the expected output.
Here's an example:
const crypto = require("crypto");
function encrypt(plainText, key, outputEncoding = "base64") {
const cipher = crypto.createCipheriv("aes-128-ecb", key, null);
return Buffer.concat([cipher.update(plainText), cipher.final()]).toString(outputEncoding);
}
function decrypt(cipherText, key, outputEncoding = "utf8") {
const cipher = crypto.createDecipheriv("aes-128-ecb", key, null);
return Buffer.concat([cipher.update(cipherText), cipher.final()]).toString(outputEncoding);
}
const key = "1000060000000000";
const plainText = "9";
const encrypted = encrypt(plainText, key, "base64");
console.log("Encrypted string (base64):", encrypted);
const decrypted = decrypt(Buffer.from(encrypted, "base64"), key, "utf8")
console.log("Decrypted string:", decrypted);
The output will be
EtgITaHs6lEvEHBipj08Kg==
It is late but will help others
You can pass any Algorithm i.e. aes-128-cbc , aes-128-ecb
Create a new file and name it as aes-service.js in service folder or anywhere in Node.js application
aes-service.js
const crypto = require('crypto');
const cryptkey = 'C51GH00SE8499727';
const iv = 'BDA30EGDH1578F81';
async function encrypt(text){
try {
var cipher = crypto.createCipheriv('aes-128-cbc',cryptkey,iv);
var crypted = cipher.update(text,'utf8','base64'); //base64 , hex
crypted += cipher.final('base64');
return crypted;
} catch (err) {
console.error('encrypt error',err);
return null;
}
}
async function decrypt(encryptdata){
//Check all Algorithms
console.log(crypto.getCiphers()); // ['aes-128-cbc', 'aes-128-ccm', ...]
try {
let decipher = crypto.createDecipheriv('aes-128-cbc',cryptkey,iv)
decipher.setAutoPadding(false)
let decoded = decipher.update(encryptdata,'base64','utf8') //base64 , hex
decoded += decipher.final('utf8')
return decoded
} catch (err) {
console.error('decrypt error',err)
return null
}
}
const AesService = {
encrypt:encrypt,
decrypt:decrypt,
}
module.exports = AesService
Node.js contorller i.e abc.controller.js
//Get aes encrypted data from node.js request
const AesService = require("./services/aes-service")
exports.getAesEncryptedDatafromReq= async (req, res) => {
try{
let decryptData = ''
try{
const buffers = [];
for await (const chunk of req) {
buffers.push(chunk);
}
const dataBuffer = Buffer.concat(buffers).toString();
const jsonParsedData = JSON.parse(dataBuffer)
decryptData = jsonParsedData.data
}catch(err){}
let decryptedData = await AesService.decrypt(decryptData)
console.log('decrypted data',decryptedData)
let sendbackdata = {
"status": 0,
"anotehr_key":[
{ "dec":"0", "asc":"1"}
]
}
sendbackdata = JSON.stringify(sendbackdata)
let encryptedData = await AesService.encrypt(sendbackdata)
//Check if encrypted performed well
// let decryptedDataAgain = await AesService.decrypt(encryptedData)
//console.log('decryptedDataAgain ',decryptedDataAgain)
return res.status(201).send({"data":encryptedData})
}catch(err){
return res.status(500)
}
}
Add route
router.post("/get/aes/encrypted/data/from/req", controller.getAesEncryptedDatafromReq)

NodeJS decryption fails while using Crypto. What is the issue?

var decipher = Crypto.createDecipheriv('aes-256-cfb', 'testtesttesttesttesttest', 'testtesttesttest')
Error: Invalid key length
at new Decipheriv (crypto.js:267:16)
at Object.createDecipheriv (crypto.js:627:10)
This is the error I get. Where am I going wrong?
¿if we try another way ? as in this example:
function encryptdata(key, text) {
const hash = crypto.createHash('sha256');
hash.update(key);
const keyBytes = hash.digest();
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cfb', keyBytes, iv);
let enc = [iv, cipher.update(text, 'utf8')];
enc.push(cipher.final());
return Buffer.concat(enc).toString('base64');
}
The key needs to be exactly 32 bytes in length.
Please try the below code to see if it works.
var crypto = require('crypto');
var key = 'testtesttesttesttesttesttesttest';
try{
var cipher = crypto.createCipheriv('aes-256-cfb', key, 'testtesttesttest');
var encryptedData = cipher.update("hello", 'utf8', 'hex') + cipher.final('hex');
console.log(encryptedData);
var decipher = crypto.createDecipheriv('aes-256-cfb', key, 'testtesttesttest');
var decryptedData = decipher.update(encryptedData, 'hex', 'utf8') + decipher.final('utf8');
console.log(decryptedData);
} catch(exception) {
console.error(exception);
}

Resources