Hi guys I am wondering if anyone has experience using nodejs to decrypt using aes256.
The encrypted string is base64 encoded and the first 16 bytes has the IV.
I am trying to extract the IV like below but having problems:
var crypto = require('crypto'),
algorithm = 'aes-256-cbc',
key = '6IAVE+56U5t7USZhb+9wCcqrTyJHqAu09j0t6fBngNo=';
function decrypt(text) {
var buf = Buffer.from(text, 'base64');
var iv = buf.toString('binary', 0, 16);
//console.log(iv.length);
//var crypt = buf.toString('base64', 16);
var decipher = crypto.createDecipheriv(algorithm, key, iv);
decipher.setAutoPadding(false);
var dec = decipher.update(crypt, 'base64', 'utf-8');
dec += decipher.final('utf-8');
return dec;
}
console.log(decrypt('mIBOVqk3bDCQPupFcIWNReXrdNRnb2P+iKl35yYRgbA='));
I keep getting the "Invalid IV Length" error.
I believe the problem is that your key is in base64, when createDecipheriv is expecting another type.
From the docs:
The key is the raw key used by the algorithm and iv is an initialization vector. Both arguments must be 'utf8' encoded strings, Buffers, TypedArray, or DataViews. If the cipher does not need an initialization vector, iv may be null.
Also, from the encrypted data, you get the IV first from the first 16 bytes, then decrypt the rest of the data.
Here's what I believe you need, though the result is a little confused:
const crypto = require('crypto');
const decrypt = (textBase64, keyBase64, ivBase64) => {
const algorithm = 'aes-256-cbc';
const ivBuffer = Buffer.from(ivBase64, 'base64');
const keyBuffer = Buffer.from(keyBase64, 'base64');
const decipher = crypto.createDecipheriv(algorithm, keyBuffer, ivBuffer);
decipher.setAutoPadding(false);
let decrypted = decipher.update(textBase64, 'base64', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
const encryptedMessage = 'mIBOVqk3bDCQPupFcIWNReXrdNRnb2P+iKl35yYRgbA=';
const key = '6IAVE+56U5t7USZhb+9wCcqrTyJHqAu09j0t6fBngNo=';
const iv = Buffer.from(encryptedMessage, 'base64').slice(0, 16);
// the message comes from the bytes AFTER the IV - this is what you should decrypt
const message = Buffer.from(encryptedMessage, 'base64').slice(16);
const result = decrypt(message, key, iv);
console.log(result);
Where the result is:
I AM CONFUSED╚╚╚
Related
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
}
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
So the idea is simple but I am not sure if I am doing it correctly.
In my application, I need to use a username/password for some database connections.
The information is stored in my .bashrc file and exports, env vars.
I dont want to store them in clear text so I want to store them encrypted.
At runtime, I read the env variables, decrypt them and use them.
What I currently have is an node.js application that does the encryption,
code snippet:
const crypto = require('crypto');
const emailPassword = CLEAR_TEXT_PASSWORD;
const algorithm = 'aes-192-cbc';
const password = 'p3241';
const key = crypto.scryptSync(password, 'salt', 24);
const iv = Buffer.alloc(16, 0);
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(emailPassword, 'utf8', 'hex');
encrypted += cipher.final('hex');
console.log(encrypted);
The result of the above becomes my env variable.
Now in my consumer application, to use the env variable, I have a reverse routine from decryption before usage.
It looks like
export const decipher = (input: string) : string => {
const algorithm = 'aes-192-cbc';
const password = 'p3241';
const key = crypto.scryptSync(password, 'salt', 24);
const iv = Buffer.alloc(16, 0);
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.update(input, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
return decrypted;
}
but what I don't is all the parameters for encrypt/decrypt are in clear text in my code on the server.
Is there a better way of doing this or am I overly paranoid?
I need to encrypt data in PHP and decrypt in Node.js.
I encrypt this in PHP:
$encrypt_method = "AES-256-CBC";
$secret_key = '7CEsPlLfVXcHf2S4wsnPnfNqYa+N/D/1zCXExN4aJSs=';
$secret_iv = 'StqUaIcbO9LFZ9QiuguXR6M/BepqZDV8p1now0FA/C4=';
// hash
$key = hash('sha256', $secret_key);
// iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
$iv = substr(hash('sha256', $secret_iv), 0, 16);
$output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
$output = base64_encode($output);
result:
VU5pckRaWHA4bjNaUjU3dGhscys3QT09
And decrypt in Node.js:
var crypto = require("crypto");
const decrypt = (textBase64, keyBase64, ivBase64) => {
const algorithm = 'AES-256-CBC';
const ivBuffer = Buffer.from(ivBase64, 'base64');
const keyBuffer = Buffer.from(keyBase64, 'base64');
const decipher = crypto.createDecipheriv(algorithm, keyBuffer, ivBuffer);
decipher.setAutoPadding(false);
let decrypted = decipher.update(textBase64, 'base64', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
const encryptedMessage = 'VU5pckRaWHA4bjNaUjU3dGhscys3QT09';
const key = '7CEsPlLfVXcHf2S4wsnPnfNqYa+N/D/1zCXExN4aJSs=';
const iv = Buffer.from('StqUaIcbO9LFZ9QiuguXR6M/BepqZDV8p1now0FA/C4=', 'base64').slice(0, 16);
// the message comes from the bytes AFTER the IV - this is what you should decrypt
const message = Buffer.from(encryptedMessage, 'base64').slice(16);
const result = decrypt(message, key, iv);
res.send("Decrypted: " + result);
Error: error:0606508A:digital envelope routines:EVP_DecryptFinal_ex:data not multiple of block length
I do not understand the error message, help to make a working example.
I am totally confused why this isn't working, I am getting Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
var crypto = require('crypto');
var key = "ciw7p02f70000ysjon7gztjn7";
var pt = "72721827b4b4ee493ac09c635827c15ce014c3c3";
var encrypt = crypto.createCipher('aes256', key);
encrypt.update(pt, 'utf8', 'hex');
var encrypted = encrypt.final('hex')
var decrypt = crypto.createDecipher('aes256', key);
decrypt.update(encrypted, 'hex', 'utf8')
decrypt.final()
You can see it action using RunKit ... https://runkit.com/fredyc/bidirectional-encryption-with-nodejs
Solution via https://github.com/nodejs/node-v0.x-archive/issues/6386
// https://github.com/nodejs/node-v0.x-archive/issues/6386#issuecomment-31817919
// with createCipher / createDecipher (both deprecated) replaced with
// createCipheriv / createDecipheriv and a generated IV passed along.
var assert = require('assert');
var crypto = require('crypto');
var algorithm = 'aes256';
var inputEncoding = 'utf8';
var outputEncoding = 'hex';
var ivlength = 16 // AES blocksize
var key = Buffer.from('ciw7p02f70000ysjon7gztjn7c2x7GfJ', 'latin1'); // key must be 32 bytes for aes256
var iv = crypto.randomBytes(ivlength);
var text = '72721827b4b4ee493ac09c635827c15ce014c3c3';
console.log('Ciphering "%s" with key "%s" using %s', text, key, algorithm);
var cipher = crypto.createCipheriv(algorithm, key, iv);
var ciphered = cipher.update(text, inputEncoding, outputEncoding);
ciphered += cipher.final(outputEncoding);
var ciphertext = iv.toString(outputEncoding) + ':' + ciphered
console.log('Result in %s is "%s"', outputEncoding, ciphertext);
var components = ciphertext.split(':');
var iv_from_ciphertext = Buffer.from(components.shift(), outputEncoding);
var decipher = crypto.createDecipheriv(algorithm, key, iv_from_ciphertext);
var deciphered = decipher.update(components.join(':'), outputEncoding, inputEncoding);
deciphered += decipher.final(inputEncoding);
console.log(deciphered);
assert.equal(deciphered, text, 'Deciphered text does not match!');
the usage error is here:
// yours (incorrect)
var encrypt = crypto.createCipher('aes256', key);
encrypt.update(pt, 'utf8', 'hex');
var encrypted = encrypt.final('hex')
// correct
var encrypt = crypto.createCipher('aes256', key);
var encrypted = encrypt.update(pt, 'utf8', 'hex');
encrypted += encrypt.final('hex')
// yours (incorrect)
var decrypt = crypto.createDecipher('aes256', key);
decrypt.update(encrypted, 'hex', 'utf8')
decrypt.final()
// correct
var decrypt = crypto.createDecipher('aes256', key);
var decrypted = decrypt.update(encrypted, 'hex', 'utf8')
decrypted += decrypt.final()
but because cipher.createCipher() and cipher.createDecipher() are now deprecated and insecure, the solution above uses cipher.createCipheriv() and cipher.createDecipheriv(), instead.
The addition of a random IV protects you from leaking information if you encrypt multiple plaintexts that share the first same 16 bytes (or multiple of 16 bytes) at the start of the message. See Encrypting using AES 256, do I need IV?