How to decrypt Triple DES in Node.js - node.js

This code worked for me to encrypt, but how can I reverse it?
I have hashed the password in the database and I need to decrypt. I have both a key and a hash.
const crypto = require('crypto');
const md5 = text => {
return crypto
.createHash('md5')
.update(text)
.digest();
}
const encrypt = (text, secretKey) => {
secretKey = md5(secretKey);
console.log(secretKey.toString('base64'));
secretKey = Buffer.concat([secretKey, secretKey.slice(0, 8)]); // properly expand 3DES key from 128 bit to 192 bit
const cipher = crypto.createCipheriv('des-ede3', secretKey, '');
const encrypted = cipher.update(text, 'utf8', 'base64');
return encrypted + cipher.final('base64');
};
const encrypted = encrypt('testtext', 'testkey');
console.log(encrypted);

This code should do what you need. We'll create a decrypt function that decrypts using the same algorithm (and key obviously!):
const crypto = require('crypto');
const md5 = text => {
return crypto
.createHash('md5')
.update(text)
.digest();
}
const encrypt = (text, secretKey) => {
secretKey = md5(secretKey);
console.log(secretKey.toString('base64'));
secretKey = Buffer.concat([secretKey, secretKey.slice(0, 8)]); // properly expand 3DES key from 128 bit to 192 bit
const cipher = crypto.createCipheriv('des-ede3', secretKey, '');
const encrypted = cipher.update(text, 'utf8', 'base64');
return encrypted + cipher.final('base64');
};
const decrypt = (encryptedBase64, secretKey) => {
secretKey = md5(secretKey);
secretKey = Buffer.concat([secretKey, secretKey.slice(0, 8)]); // properly expand 3DES key from 128 bit to 192 bit
const decipher = crypto.createDecipheriv('des-ede3', secretKey, '');
let decrypted = decipher.update(encryptedBase64, 'base64');
decrypted += decipher.final();
return decrypted;
};
const encrypted = encrypt('testtext', 'testkey');
console.log("Decrypted text:", decrypt(encrypted, 'testkey'));

Related

Node JS - Encrypt / Decrypt

I have an encryption function like this
const encryptWithInitVector = (string, keyBase64, ivBase64) => {
const key = Buffer.from(keyBase64, 'base64')
const iv = Buffer.from(ivBase64, 'base64')
const cipher = crypto.createCipheriv(getAlgorithm(keyBase64), key, iv)
let encrypted = cipher.update(string, 'utf8', 'base64')
encrypted += cipher.final('base64')
return encrypted
}
that receives a string to be encoded, an AESKey and an initializationVector.
How can I make the reverse path? I want to decode the response of the function encryptWithInitVector
https://nodejs.org/api/crypto.html#crypto_crypto_createdecipheriv_algorithm_key_iv_options
Create decipher using crypto.createDecipheriv
const decryptWithInitVector = (string, keyBase64, ivBase64) => {
const key = Buffer.from(keyBase64, 'base64')
const iv = Buffer.from(ivBase64, 'base64')
const decipher = crypto.createDecipheriv(getAlgorithm(keyBase64), key, iv)
let decrypted = decipher.update(string,'base64','utf-8');
decrypted += decipher.final('utf-8');
return decrypted
}

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: createCipheriv should give same output like earlier createCipher

Currently, I am using an old version of the crypto.js to encrypt and decrypt the strings. Below is my code for encryption -
const encrypt = (password, algorithm, encMethod) => (value) => {
const cipher = crypto.createCipher(algorithm, password);
return cipher.update(JSON.stringify(value), 'utf8', encMethod)
+ cipher.final(encMethod);
};
Using the above code my string (E-mailID) p1#yopmail.com is converted to 29c68f3bad0068c44122e734367f64557112e058c8222e3fd3908e68402ce6d5
Now as createCipher is deprecated what should I do with createCipheriv to give the same output as above.
I tried to pass null as IV to function createCipheriv, but I got the error Missing IV for cipher aes-256-cbc-hmac-sha1
Yes, Finally I resolved the same using some hacks as below -
const bytesToKey = require('evp_bytestokey');
const encryptionToken = bytesToKey(SomePasswordString, null, 256, 16);
//Ciphering
const cipher = crypto.createCipheriv('aes-256-cbc-hmac-sha1', encryptionToken.key, encryptionToken.iv);
return cipher.update(JSON.stringify(##VALUEtoENCRYPT##), 'utf8', 'hex') + cipher.final('hex');
//De-Ciphering
const decipher = crypto.createDecipheriv('aes-256-cbc-hmac-sha1', encryptionToken.key, encryptionToken.iv);
return JSON.parse(decipher.update(##VALUEtoDECRYPT##, 'hex', 'utf8') + decipher.final('utf8'));
Thanks to #topaco for suggesting me to use NPM package evp_bytestokey
Not sure if this is the right / good way to do it, we can use the same key and iv to return the same encrypted and decrypted values each time. And if you do not have a key and iv in mind, you can try using 0 filled buffers as key and iv.
var crypto = require('crypto');
var algo = 'aes-256-ctr';
var key = Buffer.alloc(32);
key.fill(0);
var iv = Buffer.alloc(16);
iv.fill(0);
const encrypt = (text) => {
var cipher = crypto.createCipheriv(algo, key, iv);
var crypted = cipher.update(text, 'utf8', 'base64');
crypted += cipher.final('base64');
return crypted
};
const decrypt = (text) => {
var decipher = crypto.createDecipheriv(algo, key, iv);
var crypted = decipher.update(text, 'base64', 'utf8');
crypted += decipher.final('utf8');
return crypted;
};
var encrypted = encrypt('yourpassword');
console.log(encrypted); //pfq1CtIh+vraJ9Bw
var decrypted = decrypt('pfq1CtIh+vraJ9Bw');
console.log(decrypted); //yourpassword

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

CrytoJS AES: Encrypted string gets decrypted to empty string

I was tinkering with crypto-js and AES.
I have a seemingly straightforward piece of code which takes a plainText and encrypts it using AES with a key and an initial vector.
When I try to decrypt the encrypted text, it gets decrypted to empty string for some reason.
This is the snippet:
const { enc, AES } = require("crypto-js");
const KEY = enc.Utf8.parse("this is a key");
const IV = enc.Utf8.parse("this is initial vector");
const originalText = "someone#example.com";
const hash = AES.encrypt(originalText, KEY, { iv: IV });
const hashText = hash.toString();
console.log(`"${originalText}" was encrypted to "${hashText}"`);
const hashTextCopy = `${hashText}`;
const decrypt = AES.decrypt(hashTextCopy, KEY, { iv: IV });
const decryptText = decrypt.toString(enc.Utf8);
console.log(`"${hashTextCopy}" was decrypted to "${decryptText}"`);
The output I get is:
"someone#example.com" was encrypted to "IgyDXGNVD8IokknoZqjamG0QecGvBM/dyxx4il8gCHA="
"IgyDXGNVD8IokknoZqjamG0QecGvBM/dyxx4il8gCHA=" was decrypted to ""
Can someone explain what is going on? I have seen quite a few examples of it over the Internet and they all seem to working just fine. But here, the text is not getting decrypted.
PS: The version I am using is "crypto-js": "^3.1.9-1",
Maybe try changing your code ever so slightly, this works for me.
As stated in the comments, I believe the problem with your initial example was the key length.
const { enc, AES } = require("crypto-js");
// Keep as a string..
const KEY = "this is a key";
const IV = enc.Utf8.parse("this is initial vector");
const originalText = "someone#example.com";
const hash = AES.encrypt(originalText, KEY, { iv: IV });
const hashText = hash.toString();
console.log(`"${originalText}" was encrypted to "${hashText}"`);
const hashTextCopy = `${hashText}`;
const decrypt = AES.decrypt(hashTextCopy, KEY, { iv: IV });
const decryptText = decrypt.toString(enc.Utf8);
console.log(`"${hashTextCopy}" was decrypted to "${decryptText}"`);
This also works:
const { enc, AES } = require("crypto-js");
// 128-bit key works nicely
const KEY = enc.Hex.parse("000102030405060708090a0b0c0d0e0f");
const IV = enc.Utf8.parse("this is initial vector");
const originalText = "someone#example.com";
const hash = AES.encrypt(originalText, KEY, { iv: IV });
const hashText = hash.toString();
console.log(`"${originalText}" was encrypted to "${hashText}"`);
const hashTextCopy = `${hashText}`;
const decrypt = AES.decrypt(hashTextCopy, KEY, { iv: IV });
const decryptText = decrypt.toString(enc.Utf8);
console.log(`"${hashTextCopy}" was decrypted to "${decryptText}"`);
The key:
const KEY = enc.Utf8.parse("abcdfghi");
Will also work correctly (since it's 128-bits). 256 would work too.
const KEY = enc.Utf8.parse("abcdfghijklmnopq");
If you just use a pass phrase, a 256-bit key will be generated from it.

Resources