Heroku production server encryption and local decryption fail - node.js

I am encrypting some text with the following encrypt function on Heroku:
const crypto = require('crypto');
// function to encrypt data ....
function encrypt(KEY, text){
const cipher = crypto.createCipher('aes192', KEY);
var encrypted = cipher.update(text,'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
// function to decryt data..............
function decrypt(KEY, text){
const decipher = crypto.createDecipher('aes192', KEY)
var decrypted = decipher.update(text,'hex','utf8')
decrypted += decipher.final('utf8');
return decrypted;
}
It then saves the text I encrypted to MongoDb server. I read the encrypted value and try to decrypt it on the local machine, but get a digital envelope routines:EVP_DecryptFinal_ex:bad decrypt. I have spent so much time trying to figure out what is the issue.
Both in Heroku and locally I am using the same key. If I try this code locally (i.e. encrypt and decrypt locally), then everything works as expected.
Do you have an idea of what might be going wrong?
I have noticed that heroku server is in United States, while I am in UK. Does the timezone play any role in here?

Related

How to decrypt a value in frontend which is encrypted in the backend (nodejs)?

Backend developers have encrypted a value in nodejs using crypto module. The code is shown below:
const _encrypt = async function(text){
var cipher = crypto.createCipher('aes-256-cbc','123|a123123123123123#&12')
var crypted = cipher.update(text,'utf8','hex')
crypted += cipher.final('hex');
console.log("in generic function....encrpted val", crypted)
return crypted;
}
I need to decrypt this value in the front end (Angular). So I tried decrypting like below:
let bytes = CryptoJS.AES.decrypt("e0912c26238f29604f5998fa1fbc78f6",'123|a123123123123123#&12');
if(bytes.toString()){
let m = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
console.log("data ",m);
}
using hardcoded value. But Im getting Error: Malformed UTF-8 data error. Can anybody please tell me how to decrypt this in angular side?
This is a tricky enough one.. the crypto.createCipher function creates a key and IV from the password you provide (See the createCipher documentation for details).
This is implemented using the OpenSSL function EVP_BytesToKey.
A JavaScript implementation is available here: openssl-file.. we'll use this to get a key and IV from the password.
So there are two steps here:
Get a key and IV from your password.
Use these with Crypto.js to decode your encoded string.
Step 1: Get key and IV (Run in Node.js )
const EVP_BytesToKey = require('openssl-file').EVP_BytesToKey;
const result = EVP_BytesToKey(
'123|a123123123123123#&12',
null,
32,
'MD5',
16
);
console.log('key:', result.key.toString('hex'));
console.log('iv:', result.iv.toString('hex'));
Step 2: Decrypt string:
const encryptedValues = ['e0912c26238f29604f5998fa1fbc78f6', '0888e0558c3bce328cd7cda17e045769'];
// The results of putting the password '123|a123123123123123#&12' through EVP_BytesToKey
const key = '18bcd0b950de300fb873788958fde988fec9b478a936a3061575b16f79977d5b';
const IV = '2e11075e7b38fa20e192bc7089ccf32b';
for(let encrypted of encryptedValues) {
const decrypted = CryptoJS.AES.decrypt({ ciphertext: CryptoJS.enc.Hex.parse(encrypted) }, CryptoJS.enc.Hex.parse(key), {
iv: CryptoJS.enc.Hex.parse(IV),
mode: CryptoJS.mode.CBC
});
console.log('Ciphertext:', encrypted);
console.log('Plain text:', decrypted.toString(CryptoJS.enc.Utf8));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
Note that if you change the password you need to generate a new key and iv using EVP_BytesToKey.
I should note that createCipher is now deprecated, so use with caution. The same applies to EVP_BytesToKey.

NodeJS crypto fails to correctly decrypt the one that was encrypted by some other tools

I used https://encode-decode.com/aes-256-ctr-encrypt-online/ to encrypt the plain text and then use nodejs crypto aes-2556-ctr algorithm to decrypt but it doesn't return the original text.
plain text: test
Key: 12345678901234567890123456789012
encrypted text using https://encode-decode.com/aes-256-ctr-encrypt-online/: D/EU6g==
Following is the code that I used in nodejs:
var crypto = require('crypto'),
algorithm = 'aes-256-ctr',
key = '12345678901234567890123456789012';
function encrypt(text){
var cipher = crypto.createCipher(algorithm,key);
var crypted = cipher.update(text,'uft8', 'base64');
crypted += cipher.final('base64');
return crypted;
}
function decrypt(text){
var decipher = crypto.createDecipher(algorithm,key);
var dec = decipher.update(text, 'base64', 'utf8');
dec += decipher.final('utf8');
return dec;
}
I can also see that nodejs encrypt returns the different output that the tool I am using to encrypt.
Does anyone know what might be missing here?
Using createCipheriv worked for me. Thanks!

Decrypt s3 file unloaded using unload command with symmetric key encryption

I tried decrypting a file from s3 which was uploaded by unload command from redshift with AES symmetric key encryption.
If we use the AWS java sdk to download with aes key given to the s3 client it works fine.But if we try to manually decrypt it after downloading the file it gives javax.crypto.BadPaddingException: Given final block not properly padded error.
The reason for manually decrypting the file is i want to decrypt the file using node.js and as far as i know there is no sdk in node that can do this directly.
Node.js code that i tried:
var AWS = require('aws-sdk');
var fs = require('fs');
var crypto = require('crypto');
var CryptoJS = require("crypto-js");
var algorithm = 'aes256';
var inputEncoding = 'hex';
var outputEncoding = 'utf-8';
var key = "symmetric key base 64"; //prod
var data = fs.readFileSync('/tmp/files/myfile');
console.log(data);
var decipher = crypto.createDecipher(algorithm,key);
var deciphered = decipher.update(data, inputEncoding, outputEncoding);
console.log(deciphered);
deciphered += decipher.final(outputEncoding);
console.log(deciphered);
When i try this i get this error: Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
So Redshift uses the envelope encryption the same way as the AWS SDK uses envelope encryption to store files on S3. So in order to decrypt the file you should:
Get the encrypted data key and the iv from the S3 object metadata (x-amz-meta-x-amz-key and x-amz-meta-x-amz-iv respectively)
Decrypt that x-amz-meta-x-amz-key value using your symmetric key using AES256 ECB mode => var decipher = crypto.createDecipheriv('AES-128-ECB',key,'');
Then decrypt '0000_part_00' using AES256 CBC mode with iv set to iv from step1 and the key set to the result of step 2. => crypto.createDecipheriv('aes-128-cbc', key, iv)
Remove padding (should be able to use cipher.setAutoPadding(true) if Node.js Crypo, what's the default padding for AES? is correct)
I haven't coded it in nodejs but I have successfully used these steps in Python
step2 step 3+4

NodeJS | Crypto Encryption is not producing correct results

I have a tricky problem to resolve. Not sure how to explain it correctly but will try my best. So here is what I am trying to do: I am trying to use a 3rd Party API, which wants me to encrypt a value and submits it. I successfully achieved it through C# code using the following block:
public string Encrypt(byte[] dataToEncrypt, byte[] keyBytes)
{
AesManaged tdes = new AesManaged();
tdes.KeySize = 256;
tdes.BlockSize = 128;
tdes.Key = keyBytes;
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7;
ICryptoTransform crypt = tdes.CreateEncryptor();
byte[] cipher = crypt.TransformFinalBlock(dataToEncrypt, 0, dataToEncrypt.Length);
tdes.Clear();
return Convert.ToBase64String(cipher, 0, cipher.Length);
}
Now, I am trying to achieve the same in Node. I wrote the following function.
encrypt(buffer){
var buffbytes = new Buffer('my app key goes here to be used as password','utf8'); //converts the app key into buffer stream
return this.encrypt_key(new Buffer(buffer,'utf8'), buffbytes);
},
encrypt_key(buffer, keybytes){
var cipher = crypto.createCipher('aes-128-ecb',keybytes);
var crypted = cipher.update(buffer,'utf8','base64');
crypted = crypted+ cipher.final('base64');
return crypted;
},
This encryption code works fine. It encrypts it fine, but it doesn't encrypt it similar to what c# code does. When I take the encrypted text from C# code, and inject the encrypted result into the API call, it passes through fine, but when I use my encrypted result into the API call, it fails mentioning that the format of my key is incorrect.
I would like to know if these code blocks are same or not. I assume it is same, because both code using 128 bit AES, ECB Cipher and default padding for Crypto Node module is PKCS5 which is same as PKCS7 for 128 bit encryption. Please Help!
Edit: 9/19/2017
Fixed as per #smarx solution:
encrypt(buffer){
var buffbytes = new Buffer(helper.Constants.AppKey,'utf8'); //converts the app key into buffer stream
return this.encrypt_key(new Buffer(buffer,'utf8'), helper.Constants.AppKey);
},
encrypt_key(buffer, key){
var cipher = crypto.createCipheriv('aes-256-ecb',key,new Buffer(0));
var crypted = cipher.update(buffer,'utf8','base64');
crypted = crypted+ cipher.final('base64');
console.log('printed: ', crypted);
return crypted;
},
In your Node.js code, you're using the wrong cipher algorithm. Use aes-256-ecb, since you're using a 256-bit key. Also, be sure to use createCipheriv, since createCipher expects a password from which it derives an encryption key.
One-line fix:
const cipher = crypto.createCipheriv('aes-256-ecb', key, new Buffer(0));
The below two programs produce identical output (Q9VZ73VKhW8ZvdcBzm05mw==).
C#:
var key = System.Text.Encoding.UTF8.GetBytes("abcdefghijklmnopqrstuvwxyz123456");
var data = System.Text.Encoding.UTF8.GetBytes("Hello, World!");
var aes = new AesManaged {
Key = key,
Mode = CipherMode.ECB,
};
Console.WriteLine(Convert.ToBase64String(
aes.CreateEncryptor().TransformFinalBlock(data, 0, data.Length)));
Node.js:
const crypto = require('crypto');
const key = 'abcdefghijklmnopqrstuvwxyz123456';
const data = 'Hello, World!';
const cipher = crypto.createCipheriv('aes-256-ecb', key, new Buffer(0));
console.log(cipher.update(data, 'utf-8', 'base64') + cipher.final('base64'));

Is it secure to encrypt and save third party API secret of users in Database?

I want to store third party API secret (like AWS) of my users in database. Leakage of which can lead to a potential loss to my users. It should be kept confidential.
So how can I secure their data on NodeJS platform? I'm thinking to encrypt and store the store the secret key so it will not directly visible to intruders.
Any suggestion on how more security can be maintained?
var crypto = require('crypto');
var assert = require('assert');
var algorithm = 'aes256'; // or any other algorithm supported by OpenSSL
var key = 'password';
var text = 'I love kittens';
var cipher = crypto.createCipher(algorithm, key);
var encrypted = cipher.update(text, 'utf8', 'hex') + cipher.final('hex');
var decipher = crypto.createDecipher(algorithm, key);
var decrypted = decipher.update(encrypted, 'hex', 'utf8') +
decipher.final('utf8');
console.log(encrypted);
console.log(decrypted);
assert.equal(decrypted, text);
Using crypto.createCipheriv is defiantly one way to keep it more secure. Generate a random IV and store it. Also you can add salt to make your encryption more secure.

Resources