How to decrypt on frontend which is encrypted on backend using crypto - javascript

Data is encrypted with the below code, is there any option to decrypt on the front-end if know the key and iv?
const algorithm = 'aes-256-ctr';
// const algorithm = 'aes-128-cbc';
let inVec = crypto.randomBytes(16);
const secret = 'salted';
const key = crypto.createHash('sha256').update(String(secret)).digest('base64').substr(0, 32);
const cipher = crypto.createCipheriv(algorithm, key, key.substr(0, 16))
let encrypt = cipher.update(name, 'utf8', 'base64')
encrypt = encrypt + cipher.final();

Related

Can't verify signatuire in 2nd program using nodejs crypto

I have two node-js programs. I want to sign a number data in the first program (node1.js) and verify that the in the second program (node2.js). But it is not happening. I think it may be because the way I'm sending the argument in write() is not correct. Please help. Thank You
node1.js
function between(min, max) {
return Math.floor(
Math.random() * (max - min) + min
)
}
var no = between(10, 100);
var crypto = require('crypto');
const node1PrivateKey = "-----BEGIN PRIVATE KEY-----\nMH4CAQAwEAYHKoZIzj0CAQYFK4EEAAMEZzBlAgEBBB4BfZ9QrDb9rRZB+sz5rkcK8VhUAJyJRj6KlOlEVYChQAM+AARylnQB587rYJlx/BvMIdtmLoMfzrCcYzrSMWxSX0tseCpunG03pBnE1mDuIo8lCnxm0kZYC4PUrr9r4f8=\n-----END PRIVATE KEY-----";
//var node1PublicKey = new Buffer("MFIwEAYHKoZIzj0CAQYFK4EEAAMDPgAEGrIPBS4+TSe1rKuuIcGItuYjAgVmIaILaa0MTIcTb7sth05hYtWKwfJA48ZTFQi5iBCH1RqCZyq+nZmB", "hex");
//keypair is correct and generated using other program
const sign = crypto.createSign('SHA256');
sign.write('no');
sign.end();
const signature = sign.sign(node1PrivateKey, 'hex');
module.exports = {
node1_no: no,
node1_signature: signature
}
node2.js
var crypto = require('crypto');
const node1PublicKey = "-----BEGIN PUBLIC KEY-----\nMFIwEAYHKoZIzj0CAQYFK4EEAAMDPgAEGrIPBS4+TSe1rKuuIcGItuYjAgVmIaILaa0MTIcTb7sth05hYtWKwfJA48ZTFQi5iBCH1RqCZyq+nZmB\n-----END PUBLIC KEY-----";
var node1 = require('./node1.js');
console.log(node1.node1_no);
console.log(node1.node1_signature);
const verify = crypto.createVerify('SHA256');
verify.write('node1.node1_no');
verify.end();
console.log(verify.verify(node1PublicKey, node1.node1_signature, 'hex'));
It returns FALSE.
The expected result is TRUE. when I use same variable name ('no') it returns TRUE.
Finally solved. The problem was write() function accepts only string. But I were sending integer value.
Now, I converted integer to string ans pass the value to write() function and it worked.
node1.js
function between(min, max) {
return Math.floor(
Math.random() * (max - min) + min
)
}
var no = between(10, 100);
var no_str = veh.toString();
var crypto = require('crypto');
const node1PrivateKey = "-----BEGIN PRIVATE KEY-----\nMH4CAQAwEAYHKoZIzj0CAQYFK4EEAAMEZzBlAgEBBB4BfZ9QrDb9rRZB+sz5rkcK8VhUAJyJRj6KlOlEVYChQAM+AARylnQB587rYJlx/BvMIdtmLoMfzrCcYzrSMWxSX0tseCpunG03pBnE1mDuIo8lCnxm0kZYC4PUrr9r4f8=\n-----END PRIVATE KEY-----";
//var node1PublicKey = new Buffer("MFIwEAYHKoZIzj0CAQYFK4EEAAMDPgAEGrIPBS4+TSe1rKuuIcGItuYjAgVmIaILaa0MTIcTb7sth05hYtWKwfJA48ZTFQi5iBCH1RqCZyq+nZmB", "hex");
const sign = crypto.createSign('SHA256');
sign.write('no_str');
sign.end();
const signature = sign.sign(node1PrivateKey, 'hex');
module.exports = {
node1_no: no,
node1_signature: signature
}
second.js
var crypto = require('crypto');
const node1PublicKey = "-----BEGIN PUBLIC KEY-----\nMFIwEAYHKoZIzj0CAQYFK4EEAAMDPgAEGrIPBS4+TSe1rKuuIcGItuYjAgVmIaILaa0MTIcTb7sth05hYtWKwfJA48ZTFQi5iBCH1RqCZyq+nZmB\n-----END PUBLIC KEY-----";
var node1 = require('./node1.js');
var no_string = node1.node1_veh
const verify = crypto.createVerify('SHA256');
verify.write('no_string');
verify.end();
console.log(verify.verify(node1PublicKey, node1.node1_signature, 'hex'));

decrypting CyproJS AES encryption in Cryptodome python

I am trying to decrypt message (in python) that has been encoded using CryptoJS in JavaScript. I have created an API in python, to post data I am using postman pre-request script.
The Error I am getting:
ValueError: Data must be padded to 16 byte boundary in CBC mode
JavaScript code for encryption
var data = {"feature_0": 0,
"feature_1": 0,
"feature_2": 0,
"feature_3": 0,
"feature_4": 0,
"feature_5": 0
};
let password = "lazydog";
let salt = "salt";
let iterations = 128;
data = JSON.stringify(data);
let len = 16 - ((data.length) % 16);
data += len.toString().repeat(len); --> removed (as suggested)
let bytes = CryptoJS.PBKDF2(password, salt, { keySize: 48, iterations: iterations });
let iv = CryptoJS.enc.Hex.parse(bytes.toString().slice(0, 32));
let key = CryptoJS.enc.Hex.parse(bytes.toString().slice(32, 96));
let encrypted = CryptoJS.AES.encrypt(data, key, {iv: iv}); //, mode: CryptoJS.mode.CBC
//encrypted = btoa(encrypted); --> removed (as suggested)
encrypted = encrypted.toString() -->added (as suggested)
postman.setGlobalVariable("data", encrypted);
python code for decryption:
def decode(encrypted):
data = b64decode(encrypted)
byte = PBKDF2("lazydog".encode("utf-8"), "salt".encode("utf-8"), 48, 128)
iv = byte[0:16]
key = byte[16:48]
cipher = AES.new(key, AES.MODE_CBC, iv)
text = cipher.decrypt(data) ## error is at this line
text = text[:-text[-1]].decode("utf-8")
return text
As the error said padding problem I added padding in JS code. Still I am not getting good results.
What's wrong I am doing here?
The encrypted string has posted to API which has written in python.
I don't know why but when encrypted passed to python '+' chars are being replaced with ' '(space). By replacing the spaces with '+' char I resolved the problem.
code
var data = {"feature_0": 0,
"feature_1": 0,
"feature_2": 0,
"feature_3": 0,
"feature_4": 0,
"feature_5": 0
};
let password = "lazydog";
let salt = "salt";
let iterations = 128;
data = JSON.stringify(data);
let bytes = CryptoJS.PBKDF2(password, salt, { keySize: 48, iterations:
iterations });
let iv = CryptoJS.enc.Hex.parse(bytes.toString().slice(0, 32));
let key = CryptoJS.enc.Hex.parse(bytes.toString().slice(32, 96));
let encrypted = CryptoJS.AES.encrypt(data, key, {iv: iv});
encrypted = encrypted.toString()
postman.setGlobalVariable("data", encrypted);
python code
def decode(encrypted):
encrypted = encrypted.replace(' ', '+') --> this line is added
data = b64decode(encrypted)
byte = PBKDF2("lazydog".encode("utf-8"), "salt".encode("utf-8"), 48, 128)
iv = byte[0:16]
key = byte[16:48]
cipher = AES.new(key, AES.MODE_CBC, iv)
text = cipher.decrypt(data) ## error is at this line
text = text[:-text[-1]].decode("utf-8")
return text
As CryptoJs pads data implicitly custom padding has been removed. And removed btoa (which is not required). Then encrypted data is converted to String. suggested by #Topaco in the comments

AES Encryption with C# Decryption with crypto-js

I'm triying to Encrypt string with C# and decrypt it using Angular crypto-js library but it's giving me different output.
I tried different c# aes encryption implementations but crypto-js library can't decrypt the encrypted data in c#. Thank you for any help.
Here is my code
Program.cs
static void Main()
{
var r = EncryptString("exampleString", "examplePassword");
Console.Write(r);
}
public static string EncryptString(string plainText, string passPhrase)
{
if (string.IsNullOrEmpty(plainText))
{
return "";
}
// generate salt
byte[] key, iv;
var salt = new byte[8];
var rng = new RNGCryptoServiceProvider();
rng.GetNonZeroBytes(salt);
DeriveKeyAndIv(passPhrase, salt, out key, out iv);
// encrypt bytes
var encryptedBytes = EncryptStringToBytesAes(plainText, key, iv);
// add salt as first 8 bytes
var encryptedBytesWithSalt = new byte[salt.Length + encryptedBytes.Length + 8];
Buffer.BlockCopy(Encoding.ASCII.GetBytes("Salted__"), 0, encryptedBytesWithSalt, 0, 8);
Buffer.BlockCopy(salt, 0, encryptedBytesWithSalt, 8, salt.Length);
Buffer.BlockCopy(encryptedBytes, 0, encryptedBytesWithSalt, salt.Length + 8, encryptedBytes.Length);
// base64 encode
return Convert.ToBase64String(encryptedBytesWithSalt);
}
private static void DeriveKeyAndIv(string passPhrase, byte[] salt, out byte[] key, out byte[] iv)
{
// generate key and iv
var concatenatedHashes = new List<byte>(48);
var password = Encoding.UTF8.GetBytes(passPhrase);
var currentHash = new byte[0];
var md5 = MD5.Create();
bool enoughBytesForKey = false;
// See http://www.openssl.org/docs/crypto/EVP_BytesToKey.html#KEY_DERIVATION_ALGORITHM
while (!enoughBytesForKey)
{
var preHashLength = currentHash.Length + password.Length + salt.Length;
var preHash = new byte[preHashLength];
Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length);
Buffer.BlockCopy(password, 0, preHash, currentHash.Length, password.Length);
Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + password.Length, salt.Length);
currentHash = md5.ComputeHash(preHash);
concatenatedHashes.AddRange(currentHash);
if (concatenatedHashes.Count >= 48)
enoughBytesForKey = true;
}
key = new byte[32];
iv = new byte[16];
concatenatedHashes.CopyTo(0, key, 0, 32);
concatenatedHashes.CopyTo(32, iv, 0, 16);
md5.Clear();
}
static byte[] EncryptStringToBytesAes(string plainText, byte[] key, byte[] iv)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (key == null || key.Length <= 0)
throw new ArgumentNullException("key");
if (iv == null || iv.Length <= 0)
throw new ArgumentNullException("iv");
// Declare the stream used to encrypt to an in memory
// array of bytes.
MemoryStream msEncrypt;
// Declare the RijndaelManaged object
// used to encrypt the data.
RijndaelManaged aesAlg = null;
try
{
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged { Mode = CipherMode.CBC, KeySize = 256, BlockSize = 128, Key = key, IV = iv };
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
msEncrypt = new MemoryStream();
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
swEncrypt.Flush();
swEncrypt.Close();
}
}
}
finally
{
// Clear the RijndaelManaged object.
aesAlg?.Clear();
}
// Return the encrypted bytes from the memory stream.
return msEncrypt.ToArray();
}
Simply decrypting it using crypto-js
let CryptoJS = require('crypto-js');
let r = CryptoJS.AES.decrypt('exampleString', 'examplePassword').toString();
The example code is attempting to decrypt the original unencrypted string, which looks to be a mistake perhaps created when trying to simplify the example code for posting the question? Either way the steps required are not too difficult, but the toString() call needs to be replaced.
var data = "U2FsdGVkX1/Zvh/5BnLfUgfbg5ROSD7Aohumr9asPM8="; // Output from C#
let r2 = CryptoJS.enc.Utf8.stringify(CryptoJS.AES.decrypt(data, 'examplePassword'));
console.log(r2);

Reproduce AES decryption method from C# in JavaScript

I am trying to reproduce the following C# decryption method in JavaScript.
This method is used to decrypt short strings: names, addresses, email addresses, etc.
It feels tantalisingly close, because the strings I have been able to "successfully" decrypt seem partially decrypted.
For instance, some of the emails look like this: x"R�Îd¹1gtWÈ2)web#example.com
CSharp
public static readonly byte[] INIT_VECTOR = { 0x00, 0x00, ... };
public static string Decrypt(string cipherText) {
string EncryptionKey = "Some Encryption Key";
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (Aes encryptor = Aes.Create())
{
​
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, INIT_VECTOR);
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
cipherText = Encoding.Unicode.GetString(ms.ToArray());
}
}
return cipherText;
}
JavaScript
import atob from 'atob';
import forge from 'node-forge';
const InitVector = [0x00, ...];
const EncryptionKey = 'Some Encryption Key';
const iv = Buffer.from(InitVector).toString();
const convertBase64StringToUint8Array = input => {
const data = atob(input);
const array = Uint8Array.from(data, b => b.charCodeAt(0));
return array;
};
const decrypt = cipher => {
const cipherArray = convertBase64StringToUint8Array(cipher);
const key = forge.pkcs5.pbkdf2(EncryptionKey, iv, 1000, 32);
const decipher = forge.cipher.createDecipher('AES-CBC', key);
decipher.start({ iv });
decipher.update(forge.util.createBuffer(cipherArray, 'raw'));
const result = decipher.finish();
if (result) {
return decipher.output.data;
} else {
return false;
}
};
Thanks to kelalaka I managed to figure this out!
This was the code I ended up with.
import atob from 'atob';
import forge from 'node-forge';
const InitVector = [0x00, ...];
const EncryptionKey = 'Some Encryption Key';
const initKey = Buffer.from(InitVector).toString(); // Changed this to `initKey`
const convertBase64StringToUint8Array = input => {
const data = atob(input);
const array = Uint8Array.from(data, b => b.charCodeAt(0));
return array;
};
const decrypt = cipher => {
const cipherArray = convertBase64StringToUint8Array(cipher);
const key = forge.pkcs5.pbkdf2(EncryptionKey, iv, 1000, 32);
/**
* Added the following
* Note the key size = 48
* This was due to the fact that the C# dictated that
* the IV was 16 bytes, starting at the end of the key.
*/
const keyAndIV = forge.pkcs5.pbkdf2(encryptionKey, initKey, 1000, 32 + 16);
/**
* Therefore, we cut the iv from the new string
*/
const iv = keyAndIV.slice(32, 32 + 16); // 16 bytes
const decipher = forge.cipher.createDecipher(
'AES-CBC',
forge.util.createBuffer(key)
);
decipher.start({ iv });
decipher.update(forge.util.createBuffer(cipherArray, 'raw'));
const result = decipher.finish();
if (result) {
return decipher.output.data;
} else {
return false;
}
};

Crypto++ and node.js combination

I'm having problems with getting data encrypted in c++, sent to Node.js server and decrypt it there. I'm using Crypto++ which works fine if I encrypt it and decrypt it. I tried various modes but nothing seemed to help.
I set key as 32x 'A' and IV as 16x '\0' just for getting consistent data
This is code in c++
AutoSeededRandomPool rand;
// Generate a random key
SecByteBlock key(0x00, AES::MAX_KEYLENGTH);
//rand.GenerateBlock(key, key.size());
memset(key.BytePtr(), 'A', key.size());
// Generate a random IV
byte iv[AES::BLOCKSIZE];
//rand.GenerateBlock(iv, AES::BLOCKSIZE);
memset(iv, 0, AES::BLOCKSIZE);
char plainText[] = "AAAAAAAAAAAAAAA";
int messageLen = (int)strlen(plainText) + 1;
CFB_Mode<AES>::Encryption cfbEncryption(key, key.size(), iv);
cfbEncryption.ProcessData((byte*)plainText, (byte*)plainText, messageLen);
/*CFB_Mode<AES>::Decryption cfbDecryption(key, key.size(), iv);
cfbDecryption.ProcessData((byte*)plainText, (byte*)plainText, messageLen);*/
unsigned int messageLength = messageLen + key.size();
const auto testData = std::vector<byte>(sizeof(unsigned int) + messageLength);
memcpy((void*)&testData[0], reinterpret_cast<void*>(&messageLength), sizeof(unsigned int));
memcpy((void*)&testData[4], (void*)key.BytePtr(), key.size());
memcpy((void*)&testData[4+key.size()], (void*)plainText, messageLen);
testClient.Send(testData);
testClient.Disconnect();
And this is the code in Node.js
socket.on('data', (data) => {
var messageSizeBuffer = data.slice(0, 4);
var messageKeyBuffer = data.slice(4, 36);
var messageDataBuffer = data.slice(36);
var decipher = crypto.createDecipher('AES-256-CFB', messageKeyBuffer)
var dec = Buffer.concat([decipher.update(messageDataBuffer) , decipher.final()]);
console.log(dec.toString());
});
I needed to use createDecipheriv and provide the same Initialization Vector as used in encryption. Beware of hardcoded key and iv, since this is used only for getting consistent data on the other side. Use random generated key and iv.
Code looks like this now
C++
AutoSeededRandomPool rand;
// Generate a random key
SecByteBlock key(0x00, AES::MAX_KEYLENGTH);
//rand.GenerateBlock(key, key.size());
memset(key.BytePtr(), 'A', key.size());
// Generate a random IV
byte iv[AES::BLOCKSIZE];
//rand.GenerateBlock(iv, AES::BLOCKSIZE);
memset(iv, 0, AES::BLOCKSIZE);
char plainText[] = "AAAAAAAAAAAAAAA";
int messageLen = (int)strlen(plainText) + 1;
CFB_Mode<AES>::Encryption cfbEncryption(key, key.size(), iv);
cfbEncryption.ProcessData((byte*)plainText, (byte*)plainText, messageLen);
unsigned int messageLength = messageLen + key.size() + AES::BLOCKSIZE;
const auto testData = std::vector<byte>(sizeof(unsigned int) + messageLength);
auto currentIndex = 0;
memcpy((void*)&testData[currentIndex], reinterpret_cast<void*>(&messageLength), sizeof(unsigned int));
currentIndex += sizeof(unsigned int);
memcpy((void*)&testData[currentIndex], (void*)key.BytePtr(), key.size());
currentIndex += key.size();
memcpy((void*)&testData[currentIndex], iv, AES::BLOCKSIZE);
currentIndex += AES::BLOCKSIZE;
memcpy((void*)&testData[currentIndex], (void*)plainText, messageLen);
testClient.Send(testData);
testClient.Disconnect();
Node.js
socket.on('data', (data) => {
var messageSizeBuffer = data.slice(0, 4);
var messageKeyBuffer = data.slice(4, 36);
var messageIvBuffer = data.slice(36, 52);
var messageDataBuffer = data.slice(52);
var decipher = crypto.createDecipheriv('AES-256-CFB', messageKeyBuffer, messageIvBuffer)
var dec = Buffer.concat([decipher.update(messageDataBuffer) , decipher.final()]);
console.log(dec.toString());
});

Categories

Resources