I need to create an encryption and decryption function in my NodeJS application. Can anyone help and point me in the right direction?
After a bit of digging, I was able to answer my own question... Sorry for the lack of clarity and detail. Will work on that for the next question.
I've included the functions to encrypt and decrypt, and the "helping" functions to generate a key and generate the initialization vector as well.
var crypto = require('crypto');
var encrypt = function encrypt(input, password) {
var key = generateKey(password);
var initializationVector = generateInitializationVector(password);
var data = new Buffer(input.toString(), 'utf8').toString('binary');
var cipher = crypto.createCipheriv('aes-256-cbc', key, initializationVector.slice(0,16));
var encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
var encoded = new Buffer(encrypted, 'binary').toString('base64');
return encoded;
};
var decrypt = function decrypt(input, password) {
var key = generateKey(password);
var initializationVector = generateInitializationVector(password);
var input = input.replace(/\-/g, '+').replace(/_/g, '/');
var edata = new Buffer(input, 'base64').toString('binary');
var decipher = crypto.createDecipheriv('aes-256-cbc', key, initializationVector.slice(0,16));
var decrypted = decipher.update(edata, 'hex', 'utf8');
decrypted += decipher.final('utf8');
var decoded = new Buffer(decrypted, 'binary').toString('utf8');
return decoded;
};
var generateKey = function generateKey(password) {
var cryptographicHash = crypto.createHash('md5');
cryptographicHash.update(password);
key = cryptographicHash.digest('hex');
return key;
}
var generateInitializationVector = function generateInitializationVector(password) {
var cryptographicHash = crypto.createHash('md5');
cryptographicHash.update(password + key);
initializationVector = cryptographicHash.digest('hex');
return initializationVector;
}
var password = 'MyPassword';
var originalStr = 'hello world!';
var encryptedStr = encrypt(originalStr, password);
var decryptedStr = decrypt(encryptedStr, password);
Hats off to adviner for the inspiration of the solution.
His post can be here: here
I had originally gotten this working with this post by dave, but that didn't work for input values with a character length greater than 15. The updated code above works for inputs of any length.
Related
const crypto = require('crypto')
class encryptedDataClass {
constructor(massage){
this.algorithm = 'aes-256-cbc'
this.initVector = crypto.randomBytes(16);
this.massage = massage;
this.Securitykey = crypto.randomBytes(32);
}
encrypted(){
const cipher = crypto.createCipheriv(this.algorithm, this.Securitykey, this.initVector);
let encryptedData = cipher.update(this.massage, "utf-8", "hex");
return encryptedData += cipher.final("hex");
}
decrypted(){
const decipher = crypto.createDecipheriv(this.algorithm, this.Securitykey,
this.initVector);
let decryptedData = decipher.update(this.massage, "hex", "utf-8");
return decryptedData += decipher.final("utf-8");
}
}
const secureName = new
encryptedDataClass("850749d212e39c8e24aee37bbb43e3c1eaee69ea592eeaeb93da5c83437f64a0")
console.log(secureName.decrypted())
I created that key using encrypted function but I can't decode that key, I'm getting an error:
06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
How can I fix this error?
As AKX mentioned, you're creating a new IV everytime and when decrypting, you need the original IV. See my solution below. I'm storing the IV and later retrieving it by splitting the string on the "."
const crypto = require("crypto");
const algorithm = "aes-192-cbc"; // algorithm to use
const password = 'defineaPassword';
const key = crypto.scryptSync(password, 'salt', 24); // create key
const iv = crypto.randomBytes(16); // generate different ciphertext everytime
module.exports = {
encryptAndBase64 (messagetext) {
try {
const iv = crypto.randomBytes(16); // generate different ciphertext everytime
const cipher = crypto.createCipheriv(algorithm, key, iv);
const ciphertext = cipher.update(messagetext, 'utf8', 'hex') + cipher.final('hex'); // encrypted text
return `${iv.toString('base64')}.${ciphertext}`;
} catch (e) {
console.log(`error while encrypting: ${e.message}`);
return messagetext;
}
},
decrypt (messagetext) {
const split = messagetext.split('.');
const iv = split[0];
messagetext = split[1];
const decipher = crypto.createDecipheriv(algorithm, key, Buffer.from(iv, 'base64'));
return decipher.update(messagetext, 'hex', 'utf8') + decipher.final('utf8'); // deciphered text
},
};
I am using the getReport operation to fetch the documentId, which later I use to download the report document itself which is encrypted and compressed.
The code looks like this:
const documentData = await this.sellingPartner.callAPI({
operation: "getReportDocument",
endpoint: "reports",
path: { reportDocumentId: reportData.reportDocumentId }
})
const request = https.get(documentData.url, function(res) {
const data = [];
res.on("data", chunk => data.push(chunk));
res.on("end", () => {
const key = new Buffer.from(documentData.encryptionDetails.key, 'base64')
const initializationVector = new Buffer.from(documentData.encryptionDetails.initializationVector, 'base64')
const input = Buffer.concat(data)
let result;
try {
result = aes.decryptText(
aes.CIPHERS.AES_256,
key,
initializationVector,
input
)
} catch (e) {
console.log(e)
}
console.log(">>>>")
console.log(result)
zlib.gunzip(result, (err, unzipped) => {
debugger
});
});
}
The current error I am getting is from zlib:
Error: incorrect header check
at Zlib.zlibOnError [as onerror] (node:zlib:189:17)
I am getting the same even if I pass the unencrypted value directly to zlib.
There is a Sample Java code example in the docs, but I cannot understand very well where they do the decryption: before unzipping or after?
In any case, what is the right way to solve this: unzip and decrypt or decrypt and unzip? The former does not work at all, the latter almost works but fails at the unzipping part.
How can I solve the unzip problem?
Short Answer:
Decrypt first and then decompress if compression type is specified.
Longer answer:
After a little research I stumbled upon this example (sadly written in python not in javascript) but it covers the steps to do in more detail.
https://github.com/amzn/selling-partner-api-docs/issues/186#issuecomment-756108550
It contains both compressed or not compressed cases.
Specifically for compressed it looks like this:
import gzip
import requests
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
def ase_cbc_decryptor(key, iv, encryption):
cipher = Cipher(algorithms.AES(base64.b64decode(key)), modes.CBC(base64.b64decode(iv)))
decryptor = cipher.decryptor()
decrypted_text = decryptor.update(encryption)
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
unpaded_text = unpadder.update(decrypted_text)
return unpaded_text + unpadder.finalize()
def get_report_document_content(self, key, iv, url, compression_type=None):
resp = requests.get(url=url)
resp_content = resp.content
decrypted_content = ase_cbc_decryptor(key=key, iv=iv, encryption=resp_content)
if compression_type == 'GZIP':
decrypted_content = gzip.decompress(decrypted_content)
code = 'utf-8'
if 'cp1252' in resp.headers.get('Content-Type', '').lower():
code = 'Cp1252'
return decrypted_content.decode(code)
P.S. keep in mind you need to use AES in CBC mode
Short example here:
https://gist.github.com/manuks/5cef1e536ef791e97b39
var keyhex = "8479768f48481eeb9c8304ce0a58481eeb9c8304ce0a5e3cb5e3cb58479768f4"; //length 32
var blockSize = 16;
function encryptAES(input) {
try {
var iv = require('crypto').randomBytes(16);
//console.info('iv',iv);
var data = new Buffer(input).toString('binary');
//console.info('data',data);
key = new Buffer(keyhex, "hex");
//console.info(key);
var cipher = require('crypto').createCipheriv('aes-256-cbc', key, iv);
// UPDATE: crypto changed in v0.10
// https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10
var nodev = process.version.match(/^v(\d+)\.(\d+)/);
var encrypted;
if( nodev[1] === '0' && parseInt(nodev[2]) < 10) {
encrypted = cipher.update(data, 'binary') + cipher.final('binary');
} else {
encrypted = cipher.update(data, 'utf8', 'binary') + cipher.final('binary');
}
var encoded = new Buffer(iv, 'binary').toString('hex') + new Buffer(encrypted, 'binary').toString('hex');
return encoded;
} catch (ex) {
// handle error
// most likely, entropy sources are drained
console.error(ex);
}
}
function decryptAES(encoded) {
var combined = new Buffer(encoded, 'hex');
key = new Buffer(keyhex, "hex");
// Create iv
var iv = new Buffer(16);
combined.copy(iv, 0, 0, 16);
edata = combined.slice(16).toString('binary');
// Decipher encrypted data
var decipher = require('crypto').createDecipheriv('aes-256-cbc', key, iv);
// UPDATE: crypto changed in v0.10
// https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10
var nodev = process.version.match(/^v(\d+)\.(\d+)/);
var decrypted, plaintext;
if( nodev[1] === '0' && parseInt(nodev[2]) < 10) {
decrypted = decipher.update(edata, 'binary') + decipher.final('binary');
plaintext = new Buffer(decrypted, 'binary').toString('utf8');
} else {
plaintext = (decipher.update(edata, 'binary', 'utf8') + decipher.final('utf8'));
}
return plaintext;
}
var input="testing";
var encrypted = encryptAES(input);
console.info('encrypted:', encrypted);
var decryped = decryptAES(encrypted);
console.info('decryped:',decryped);
This is the function used to encrypt in php
function generatetoken()
{
$token = ['id' => '123456','name' => 'username','email' => 'useremail#example.com','type' => 'user'];
$cipher = "AES-128-CBC";
$plaintext = json_encode($token);
$ivlen = openssl_cipher_iv_length($cipher = "AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, '123456789', $options = OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, '123456789', $as_binary = true);
$ciphertext = base64_encode($iv . $hmac . $ciphertext_raw);
return ciphertext;
}
I have this function to decrypt text in php :
function decodetokeninPhp($request_token)
{
$cipher = "AES-128-CBC";
$c = base64_decode($request_token);
$ivlen = openssl_cipher_iv_length($cipher = "AES-128-CBC");
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len = 32);
$ciphertext_raw = substr($c, $ivlen + $sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, '123456789', $options = OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, '123456789', $as_binary = true);
if (hash_equals($hmac, $calcmac)) {
return json_encode($original_plaintext);
} else {
return null;
}
}
I want the equivalent of this in javascript/Nodejs, i tried this:
function decode(token){
var password = 'My-key';
const ivLength = 16;
const sha2len = 32;
let replacedToken = token.toString();
const base64decoded = Buffer.from(replacedToken, 'base64').toString('binary');
const iv = replacedToken.substr(0,ivLength);
const hMac= replacedToken.substr( ivLength,sha2len);
const ciphertext_raw = replacedToken.substr(ivLength+sha2len);
var DataEncrypt = ciphertext_raw;
var DataKey = CryptoJS.enc.Utf8.parse(password);
var DataVector = CryptoJS.enc.Utf8.parse(iv);
var decrypted = CryptoJS.AES.decrypt(DataEncrypt, DataKey, { iv: DataVector });
var decrypted = CryptoJS.enc.Utf8.stringify(JSON.stringify(decrypted));
console.log("token decoded"+ decrypted);
}
But the console.log just print "token decoded:", it's empty result :(
Please someone help me i'm going crazy :/
If you want to use the crypto module of NodeJS, the whole CryptoJS part has to be changed. A possible implementation with crypto is:
var crypto = require('crypto');
function decode(token){
var keyDec = Buffer.from('0123456789012345', 'utf8'); // sample key for encryption/decryption
var keyAuth = Buffer.from('0123456789', 'utf8'); // sample key for authentication
var ivLen = 16;
var macLen = 32;
var tokenBuf = Buffer.from(token, 'base64');
var iv = tokenBuf.slice(0, ivLen);
var mac = tokenBuf.slice(ivLen, ivLen + macLen);
var ciphertext = tokenBuf.slice(ivLen + macLen);
// Authenticate
var hmac = crypto.createHmac('sha256', keyAuth);
hmac.update(ciphertext);
var macCalc = hmac.digest();
if (macCalc.equals(mac)) {
// Decrypt, if authentication is successfull
var decipher = crypto.createDecipheriv("AES-128-CBC", keyDec, iv);
var decrypted = decipher.update(ciphertext, '', 'utf-8');
decrypted += decipher.final('utf-8');
return JSON.parse(decrypted);
} else {
console.log("Decryption failed");
}
}
var token = decode('U3pukkS48yeNpsusv43Tmv2AmmDfYVtQ8jPw2izEQ0CVOfutGtA9e3ZWXJo2Ibi2axo31blnW6uq/yCz/KRSltwGhCmwpiHQ8mP5ulMf0Nr9V9Gzr6r+R6y3ZOpzTsV9IEkaKDxZTihfoDAzeyN9LYKS9uUW6URL0Do1HGaZ51o='); // from PHP code
console.log(token);
Here, the ciphertext was generated with the posted PHP code using the sample keys 0123456789012345 and 0123456789 for encryption and authentication, respectively.
I am suspicious of the json_encode() in the PHP code for decryption. Here I would expect a json_decode() and thus, in the NodeJS code a JSON.parse() (but you can modify this as needed).
I'm trying to add encryption for passwords, and want to store the hashes and have the encryption/decryption being done on signup/login, the encryption goes fine from utf8 to hex, but when I try to decrypt, I get back a bunch of weird letters that look like:
"\ufffd'\rF\ufffd\ufffd\¡\ufffd6>\ufffd\ufffd#B,0\u0005\u0007\ufffd?\ufffd;\ufffd\u0018\u001e\"oؕ"
I've been trying to figure out how it could be using the wrong encoding data, as I have the hex and utf8 tags in the right places; I've been trying to look at other posts too, but I'm afraid they go over my head over how they're supposed to help me.
This is in the first file that I export it from
var crypto = require("crypto");
var secretkey = "twinkies";
var key = {
encrypt: function(pass){
var mykey = crypto.createCipher('aes-128-cbc', secretkey);
var finpass = mykey.update(pass, 'utf8', 'hex');
finpass += mykey.final('hex');
return finpass;
},
decrypt: function(pass){
var mykey = crypto.createCipher('aes-128-cbc', secretkey);
var finpass = mykey.update(pass, 'hex', 'utf8');
finpass += mykey.final('utf8');
return finpass;
}
};
this is the second file that uses the methods
app.post("/api/users/create", function(req, res) {
console.log(req.body.pword);
var newpass = key.encrypt(req.body.pword);
var oldpass = key.decrypt(newpass);
var enddata = {
Ciphored_password : newpass,
Deciphored_password : oldpass
}
console.log(res.json(enddata));
I use postman to test my localhost api, and am putting in 'bigthonks' as the password, when it console logs the enddata the Ciphored_password looks like:
982cb6d27f65fbb642c8c7b710e6c349
and the Deciphored_password:
"\ufffd'\rF\ufffd\ufffd\¡\ufffd6>\ufffd\ufffd#B,0\u0005\u0007\ufffd?\ufffd;\ufffd\u0018\u001e\"oؕ"
It turns out I'm an idiot... I missed one of the first steps of bugfixing which is looking at methodnames,
var crypto = require("crypto");
var secretkey = "twinkies";
var key = {
encrypt: function(pass){
var mykey = crypto.createCipher('aes-128-cbc', secretkey);
var finpass = mykey.update(pass, 'utf8', 'hex');
finpass += mykey.final('hex');
return finpass;
},
decrypt: function(pass){
var mykey = crypto.createCipher('aes-128-cbc', secretkey);
var finpass = mykey.update(pass, 'hex', 'utf8');
finpass += mykey.final('utf8');
return finpass;
}
};
on my decrypt method, when I'm generating the decipher for it i use the createCipher() method instead of the createDecipher() method, really big woops on my part.
I easily fixed it by changing the decrypt method to this:
decrypt: function(pass){
var mykey = crypto.createDecipher('aes-128-cbc', secretkey);
var finpass = mykey.update(pass, 'hex', 'utf8');
finpass += mykey.final('utf8');
return finpass;
}
and now it works.
I am trying to write encrypted content to file , following this
For first call to log() it works fine,file has 0c8bd89c2e it decrypts "hello", but after second call to log it appends data to file 0c8bd89c2e0c8bd89c2e but decrypts it like hello���
What am i doing wrong here? and how to correct this ?
var fs = require('fs');
var crypto = require('crypto'),
algorithm = 'aes-256-ctr',
password = 'd6F3Efeq';
function encrypt(text){
var cipher = crypto.createCipher(algorithm,password)
var crypted = cipher.update(text,'utf8','hex')
crypted += cipher.final('hex');
return crypted;
}
function decrypt(text){
var decipher = crypto.createDecipher(algorithm,password)
var dec = decipher.update(text,'hex','utf8')
dec += decipher.final('utf8');
return dec;
}
var tmp = {
log : function(req,res){
// var data = body.UserName+" "+body.Domain+" "+body.Password+" ";
var data = "hello";
fs.appendFile("./objects/tmp.dat",encrypt(data), function(err) {
if (err)
console.error(err);
else{
console.log("done");
}
res.send();
});
},
test: function(req,res){
var data = fs.readFileSync("./objects/tmp.dat",{encoding:'utf8'});
res.send(decrypt(data));
}
}
module.exports = tmp;