How to decrypt AES 128-CBC by Crypto-JS? - javascript

I have a PHP code that decrypts payload with a secret key, I'm trying to write exactly the same code in JavaScript using the crypto-js library, but I get the wrong result.
First 16 bytes in payload - is vector, remaining part - is useful information.
Working code in PHP - https://ideone.com/NJXkRK
function getPayload($app_secret_key, $data) {
// Get the encryption key (16 first bytes of the app's client_secret key)
$encryption_key = substr($app_secret_key, 0, 16);
// Decrypt payload
$json_data = aes_128_decrypt($encryption_key, $data);
// Decode json
$json_decoded = json_decode($json_data, true);
return $json_data;
}
function aes_128_decrypt($key, $data) {
// Ecwid sends data in url-safe base64. Convert the raw data to the original base64 first
$base64_original = str_replace(array('-', '_'), array('+', '/'), $data);
// Get binary data
$decoded = base64_decode($base64_original);
// Initialization vector is the first 16 bytes of the received data
$iv = substr($decoded, 0, 16);
// The payload itself is is the rest of the received data
$payload = substr($decoded, 16);
// Decrypt raw binary payload
$json = openssl_decrypt($payload, "aes-128-cbc", $key, OPENSSL_RAW_DATA, $iv);
//$json = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $payload, MCRYPT_MODE_CBC, $iv); // You can use this instead of openssl_decrupt, if mcrypt is enabled in your system
return $json;
}
// Get payload from the GET and process it
$ecwid_payload = "ng7W9c9jLhkX7ATMpafNAd5Vt_skEaFAqnQaw0Ing1iwYQOwB0Q_CuCS8yQeHeorTdCpZWDTNrzhcq_umX7IaAFUPPgs0zyddY7Er1tA0aze5kWGHUV54fJHoVEJHMmVEi-G5g8ZnNopIFu0YQgQqLpCq8TP2zFJunSTA7VXHTmqHNAD2JXaUb-VylcJWzgV0vaCoGyHqaPbsNNw6HSWkAzhh8dLmsYB0uzsZ_zl3wVXubCL4p2N53PmNPBLCgoC";
$client_secret = "zcKf1Zt0UsO43S46Un3pxIgs91R1xMGs";
$result = getPayload($client_secret, $ecwid_payload);
print($result);
Not working JS code
function getPayload() {
const payload =
"ng7W9c9jLhkX7ATMpafNAd5Vt_skEaFAqnQaw0Ing1iwYQOwB0Q_CuCS8yQeHeorTdCpZWDTNrzhcq_umX7IaAFUPPgs0zyddY7Er1tA0aze5kWGHUV54fJHoVEJHMmVEi-G5g8ZnNopIFu0YQgQqLpCq8TP2zFJunSTA7VXHTmqHNAD2JXaUb-VylcJWzgV0vaCoGyHqaPbsNNw6HSWkAzhh8dLmsYB0uzsZ_zl3wVXubCL4p2N53PmNPBLCgoC";
const key = "zcKf1Zt0UsO43S46Un3pxIgs91R1xMGs";
// Get the encryption key (16 first bytes of the app's client_secret key)
const encryption_key = key.substr(0, 16);
// Decrypt payload
const base64_original = payload.replace(/-/gi, "+").replace(/_/gi, "/");
const data = aes_128_decrypt(encryption_key, base64_original);
console.log(data);
}
function aes_128_decrypt(password, data) {
const decoded = atob(data);
let iv = decoded.substr(0, 16);
let payload = decoded.substr(16);
iv = CryptoJS.enc.Hex.parse(iv);
const decrypted = CryptoJS.AES.decrypt(payload, password, {
iv: iv,
padding: CryptoJS.pad.NoPadding
});
return decrypted.toString();
}
window.onload = function() {
getPayload();
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
Please help me to improve this JS code

There are several issues in the JavaScript code:
The key must not be passed as a string, but as a WordArray (otherwise CryptoJS uses a key derivation function).
IV and ciphertext are not determined correctly.
The ciphertext must be passed as CipherParams object (or as Base64 encoded string).
The padding must be PKCS7 (decryption would also work with NoPadding, but the padding bytes wouldn't be removed).
The plaintext must be Utf8 decoded (.toString() hex encodes by default)
For details see the CryptoJS documentation, especially the chapters The Cipher Input and The Cipher Output.
The following JavaScript code decrypts the ciphertext:
function getPayload() {
const payload = "ng7W9c9jLhkX7ATMpafNAd5Vt_skEaFAqnQaw0Ing1iwYQOwB0Q_CuCS8yQeHeorTdCpZWDTNrzhcq_umX7IaAFUPPgs0zyddY7Er1tA0aze5kWGHUV54fJHoVEJHMmVEi-G5g8ZnNopIFu0YQgQqLpCq8TP2zFJunSTA7VXHTmqHNAD2JXaUb-VylcJWzgV0vaCoGyHqaPbsNNw6HSWkAzhh8dLmsYB0uzsZ_zl3wVXubCL4p2N53PmNPBLCgoC";
const key = "zcKf1Zt0UsO43S46Un3pxIgs91R1xMGs";
// Get the encryption key (16 first bytes of the app's client_secret key)
//const encryption_key = key.substr(0, 16);
const encryption_key = CryptoJS.enc.Utf8.parse(key.substr(0, 16)); // Parse the key into a WordArray
// Decrypt payload
const base64_original = payload.replace(/-/gi, "+").replace(/_/gi, "/");
const data = aes_128_decrypt(encryption_key, base64_original);
console.log(data.replace(/(.{56})/g,'$1\n')); // {"store_id":20553036,"access_token":"secret_a9TmTJfRt3gyvxjJ9UwYjs9VQip3F7rp","public_token":"public_QQ99gUwVGdvKuZbLLyNZzDsvXF5iF3gh","view_mode":"PAGE","lang":"ru"}
//console.log(JSON.parse(data)); // Convert JSON string into JavaScript object (optional)
}
function aes_128_decrypt(password, data) {
/*
const decoded = atob(data);
let iv = decoded.substr(0, 16);
let payload = decoded.substr(16);
iv = CryptoJS.enc.Hex.parse(iv);
*/
var ivCiphertext = CryptoJS.enc.Base64.parse(data); // Parse data into a WordArray
var iv = CryptoJS.lib.WordArray.create(ivCiphertext.words.slice(0, 16 / 4)); // Separate iv
var payload = CryptoJS.lib.WordArray.create(ivCiphertext.words.slice(16 / 4)); // and ciphertext
//const decrypted = CryptoJS.AES.decrypt(payload, password, {
const decrypted = CryptoJS.AES.decrypt(
{
ciphertext: payload // Pass CipherParams object
},
password,
{
iv: iv
//padding: CryptoJS.pad.NoPadding // Apply PKCS7 padding
});
//return decrypted.toString();
return decrypted.toString(CryptoJS.enc.Utf8); // Utf8 decode plaintext
}
window.onload = function() {
getPayload();
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

Related

How to decrypt AES256 data which was encrypted on PHP and get value in Javascript?

I have encrypted some value using aes-256-cbc mode on PHP like this:
public function encrypt(string $data): string
{
$iv = $this->getIv();
$encryptedRaw = openssl_encrypt(
$data,
$this->cryptMethod, //aes-256-cbc
$this->key,
OPENSSL_RAW_DATA,
$iv
);
$hash = hash_hmac('sha256', $encryptedRaw, $this->key, true);
return base64_encode( $iv . $hash . $encryptedRaw );
}
Then I tried to decrypt it on PHP and it works fine:
public function decrypt(string $data): string
{
$decoded = base64_decode($data);
$ivLength = openssl_cipher_iv_length($this->cryptMethod);
$iv = substr($decoded, 0, $ivLength);
$hmac = substr($decoded, $ivLength, $shaLength = 32);
$decryptedRaw = substr($decoded, $ivLength + $shaLength);
$originalData = openssl_decrypt(
$decryptedRaw,
$this->cryptMethod,
$this->key,
OPENSSL_RAW_DATA,
$iv
);
So I'm new to JavaScript and I don't know how to realize the same decrypt method as on php.
Example of encrypted string and it's key:
encrypted string lUIMFpajICh/e44Mwkr0q9xdyJh5Q8zEJHi8etax5BRl78Vsyh+wDknmBga1L8p8SDZA6WKz1CvAAREFGreRAQ== secret key - 9SJ6O6IwmItSRICbXgdJ
Example what I found returns empty string:
const decodedString = base64.decode(
`lUIMFpajICh/e44Mwkr0q9xdyJh5Q8zEJHi8etax5BRl78Vsyh+wDknmBga1L8p8SDZA6WKz1CvAAREFGreRAQ==`
);
const CryptoJS = require("crypto-js");
var key = CryptoJS.enc.Latin1.parse("9SJ6O6IwmItSRICbXgdJ");
var iv = CryptoJS.enc.Latin1.parse(decodedString.slice(0, 16));
var ctx = CryptoJS.enc.Base64.parse(
"lUIMFpajICh/e44Mwkr0q9xdyJh5Q8zEJHi8etax5BRl78Vsyh+wDknmBga1L8p8SDZA6WKz1CvAAREFGreRAQ=="
);
var enc = CryptoJS.lib.CipherParams.create({ ciphertext: ctx });
console.log(
CryptoJS.AES.decrypt(enc, key, { iv: iv }).toString(CryptoJS.enc.Utf8)
);
}
What I did wrong?
The key used in the PHP code is only 20 bytes in size and thus too small for AES-256 (AES-256 requires a 32 bytes key). PHP/OpenSSL implicitly pads the key with 0x00 values to the required key length. In the CryptoJS code, this must be done explicitly.
Furthermore, in the CryptoJS code, IV (the first 16 bytes), HMAC (the following 32 bytes) and ciphertext (the rest) are not separated correctly.
Also, the authentication is missing. To do this, the HMAC for the ciphertext must be determined using the key and compared with the HMAC sent. Decryption only takes place if authentication is successful.
If all of this is taken into account, the posted code can be fixed e.g. as follows:
var key = CryptoJS.enc.Utf8.parse("9SJ6O6IwmItSRICbXgdJ".padEnd(32, "\0")); // pad key
var ivMacCiphertext = CryptoJS.enc.Base64.parse("lUIMFpajICh/e44Mwkr0q9xdyJh5Q8zEJHi8etax5BRl78Vsyh+wDknmBga1L8p8SDZA6WKz1CvAAREFGreRAQ==")
var iv = CryptoJS.lib.WordArray.create(ivMacCiphertext.words.slice(0, 4)); // get IV
var hmac = CryptoJS.lib.WordArray.create(ivMacCiphertext.words.slice(4, 4 + 8)); // get HMAC
var ct = CryptoJS.lib.WordArray.create(ivMacCiphertext.words.slice(4 + 8)); // get Ciphertext
var hmacCalc = CryptoJS.HmacSHA256(ct, key);
if (hmac.toString() === hmacCalc.toString()) { // authenticate
var dt = CryptoJS.AES.decrypt({ciphertext: ct}, key, { iv: iv }).toString(CryptoJS.enc.Utf8); // decrypt
console.log(dt);
} else {
console.log("Decryption failed");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
A few thoughts for you:
Check that your encoding/decoding is working properly. For each stage
of the process, endode/decode, then console log the output and
compare input to output, and also between PHP and javascript.
CBC mode uses padding to fill out the blocks. Check that both stacks
are using the same padding type.
Rather than using CBC and a separate HMAC, how about jumping to AEAD (like AES
GCM) which avoids the padding issue, and also incorporates the MAC
into the encryption, so is a more simple interface?

AES encryption without IV in PHP and JS gives different result

I have the next code in PHP:
$plain = 'some string to encode';
$key = '01234567891234567890123456789012';
$cipherSuite = 'aes-128-cbc';
$iv = null; // I have to use null, I know it's not safe
$result = #openssl_encrypt($plain, $cipherSuite, $key, null, $iv); // Suppress warning of an empty IV
dd($result); // result is 9VK02Mt8IaS+Bng8SbqhCVXUc5TteHKqt3y/EbaJZ1w=
I'm trying to encode the same in online tool - https://www.devglan.com/online-tools/aes-encryption-decryption. Tool says that key must be 16 byte, so I use just half of key - 0123456789123456
It returns exact same result as PHP. Please note that IV is empty.
I need to do the same encryption (and than decription) in JS using Crypto-js
const CryptoJS = require('crypto-js');
var key = CryptoJS.lib.WordArray.create('01234567891234567890123456789012');
var iv = CryptoJS.lib.WordArray.create('');
//var iv = null;
// var iv = CryptoJS.enc.Hex.parse("");
// var iv = CryptoJS.enc.Base64.parse('');
let cfg = {
mode: CryptoJS.mode.CBC,
keySize: 128,
iv: iv
};
const body = 'some string to encode';
const encryptedBody = CryptoJS.AES.encrypt(body, key, cfg).toString();
console.log( encryptedBody );
// result is VYCEPSx9nmb0FJGf1RiU/daL5nIk/qaJZU82jrlGQws=
Similar example at https://jsfiddle.net/pj76d5ov/
Result in JS is different with PHP. Is there a way to use CryptoJS without IV ?
If I use the key as a string, CryptoJS generates IV based on my key, so I have to use WordArray type.
Then I tried to change iv to some values, but it doesn't help. Setting iv to false or null, or not sending iv at all gives an error.
In the PHP code AES-128 is specified. Therefore PHP implicitly truncates the 32 bytes key to the first 16 bytes. In the CryptoJS code only this 16 bytes key may be used.
Furthermore, key and IV are converted most easily with the CryptoJS encoders into a WordArray.
A possible CryptoJS implementation is:
var key = CryptoJS.enc.Utf8.parse('0123456789123456');
var iv = CryptoJS.enc.Hex.parse('00000000000000000000000000000000');
let cfg = {
mode: CryptoJS.mode.CBC,
keySize: 128,
iv: iv
};
const body = 'some string to encode';
const encryptedBody = CryptoJS.AES.encrypt(body, key, cfg).toString();
console.log( encryptedBody ); // result is 9VK02Mt8IaS+Bng8SbqhCVXUc5TteHKqt3y/EbaJZ1w=
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
A static IV is insecure, but you already know that.

CryptoJS AES decrypting a message encrypted in PHP openssl_encrypt

I have the following PHP code that uses openssl_encrypt() function to encrypt a message. Here is the code:
$ciphering = "AES-128-CTR";
$options = 0;
$encryption_iv = '5192001995060634';
$encryption_key = "TasKagitMakas";
function encrypt ($string) {
global $ciphering, $options, $encryption_iv, $encryption_key;
$encryption = openssl_encrypt($string, $ciphering,
$encryption_key, $options, $encryption_iv);
$encryption = strtr(base64_encode($encryption), '+/=', '-_,');
return $encryption;
}
I was trying to reverse the process above and get the Javascript implementation using CryptoJS. Here's what I came up with:
var str = "bzB5UVNBclRHbWhlQUs4aHJoMHVxR1BJNEF1Sk9BRkpvbEpBRDFnVmg0MEx4RGtqWllvdUIrSW0vZGY3eG1KMVd2b2JxRFlOTnJ6N2FnPT0,";
str = str.split("-").join("+");
str = str.split("_").join("/");
str = str.split(",").join("=");
var encrypted = CryptoJS.enc.Base64.parse(str);
var encryptedStr = encrypted.toString(CryptoJS.enc.Utf8);
var key = "TasKagitMakas";
var iv = "5192001995060634";
var decrypted = CryptoJS.AES.decrypt(encryptedStr, key, {iv: iv, mode: CryptoJS.mode.CTR});
console.log(decrypted.toString(CryptoJS.enc.Utf8));
This code gives me a blank output, just nothing. What am I doing wrong here? How can I correct my implementation?
PHP code:
openssl_encrypt pads the key with zero values until the specified key length is reached, i.e. the key TasKagitMakas is expanded to TasKagitMakas\0\0\0.
$options = 0 means that the ciphertext is implicitly Base64 encoded. Since the ciphertext is explicitly Base64 encoded again afterwards, it is Base64 encoded twice in total. This is unnecessary and should be changed, for example, with $options = OPENSSL_RAW_DATA.
For a stream cipher mode like CTR openssl_encrypt automatically disables the default PKCS7 padding.
JavaScript code:
Since it was Base64 encoded twice in the PHP code, it is necessary to Base64 decode twice in the JavaScript code. This step is of course only necessary for the unchanged PHP code.
Key and IV must be parsed into a WordArray with the Utf8 Encoder. The extended key must be used.
CryptoJS.AES.decrypt expects the ciphertext as CipherParams object.
Unlike PHP, CryptoJS does not automatically disable the default PKCS7 padding for a stream cipher mode, i.e. it must be explicitly disabled.
The following JavaScript code decrypts the ciphertext:
var str = "bzB5UVNBclRHbWhlQUs4aHJoMHVxR1BJNEF1Sk9BRkpvbEpBRDFnVmg0MEx4RGtqWllvdUIrSW0vZGY3eG1KMVd2b2JxRFlOTnJ6N2FnPT0,";
str = str.split("-").join("+");
str = str.split("_").join("/");
str = str.split(",").join("=");
var encrypted = CryptoJS.enc.Base64.parse(str); // Base64 decode twice (as long as this happens in the PHP code)
var encrypted = encrypted.toString(CryptoJS.enc.Utf8);
var encrypted = CryptoJS.enc.Base64.parse(encrypted);
var key = CryptoJS.enc.Utf8.parse("TasKagitMakas\0\0\0"); // Expand the key and use the Utf8 encoder
var iv = CryptoJS.enc.Utf8.parse("5192001995060634"); // Use the Utf8 encoder
var decrypted = CryptoJS.AES.decrypt(
{
ciphertext: encrypted // Pass teh ciphertext as CipherParams object
},
key,
{
iv: iv,
mode: CryptoJS.mode.CTR,
padding: CryptoJS.pad.NoPadding // Disable the PKCS7 padding
});
console.log(decrypted.toString(CryptoJS.enc.Utf8));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
with the output:
399002 Örnek2 Öğrenci student#ug.bilkent.edu.tr Team1 6 

How to C#.NET encrypt() then JS WebCryptoApi decrypt() using AES-GCM?

I want to encrypt data using C# and decrypt it using JS.
This table suggests that AES-GCM is the way to go with WebCryptoApi https://diafygi.github.io/webcrypto-examples/.
I am successfully using BouncyCastle https://codereview.stackexchange.com/questions/14892/simplified-secure-encryption-of-a-string to encrypt (and decrypt) in .NET.
var message = "This is the test message";
var key = AESGCM.NewKey();
Console.Out.WriteLine("KEY:" + Convert.ToBase64String(key));
>> KEY:5tgX6AOHot1T9SrImyILIendQXwfdjfOSRAVfMs0ed4=
string encrypted = AESGCM.SimpleEncrypt(message, key);
Console.Out.WriteLine("ENCRYPTED:" + encrypted);
>> ENCRYPTED:Ct0/VbOVsyp/LMxaaFqKKw91+ts+8uzDdHLrTG1XVjPNL7KiBGYB4kfdNGl+xj4fYqdb4JXgdTk=
var decrypted = AESGCM.SimpleDecrypt(encrypted, key);
Console.Out.WriteLine("DECRYPTED:" + decrypted);
>> DECRYPTED:This is the test message
But, I can't figure out how to decrypt this client side. There's a great list of WebCryptoApi examples including AES-GCM at https://github.com/diafygi/webcrypto-examples#aes-cbc---decrypt.
First step (which seems to working) is to import the key, which I have as a base-64 encoded string:
var keyString = "+6yDdIiJJl8Lqt60VOHuP25p4yNxz0CRMoE/WKA+Mqo=";
function _base64ToArrayBuffer(base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array( len );
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}
var key = _base64ToArrayBuffer(keyString )
var cryptoKey; // we'll get this out in the promise below
window.crypto.subtle.importKey(
"raw",
key,
{ //this is the algorithm options
name: "AES-GCM",
},
true, // whether the key is extractable
["encrypt", "decrypt"] // usages
)
.then(function(key){
//returns the symmetric key
console.log(key);
cryptoKey = key;
})
.catch(function(err){
console.error(err);
});
The final step should be to decrypt the encoded message, which is also a base-64 encoded string
var encryptedString = "adHb4UhM93uWyRIV6L1SrYFbxEpIbj3sQW8VwJDP7v+XoxGi6fjmucEEItP1kQWxisZp3qhoAhQ=";
var encryptedArrayBuffer = _base64ToArrayBuffer(encryptedString)
window.crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: new ArrayBuffer(12), //The initialization vector you used to encrypt
//additionalData: ArrayBuffer, //The addtionalData you used to encrypt (if any)
// tagLength: 128, //The tagLength you used to encrypt (if any)
},
cryptoKey, //from above
encryptedArrayBuffer //ArrayBuffer of the data
)
.then(function(decrypted){
//returns an ArrayBuffer containing the decrypted data
console.log(new Uint8Array(decrypted));
})
.catch(function(err){
debugger; console.error(err);
});
Unfortunately, this is thowing a DomError.
I have no idea what I am supposed to use for "iv" in the decrypt method. I've tried null, ArrayBuffer(0), ArrayBuffer(12). This is pretty much where my understanding ends.
If you look into the implementation of AESGCM, you should see that the nonce (called IV) is part of the ciphertext. Its size is set to 16 bytes (NonceBitSize = 128). You would need to read that many bytes from the beginning of the ciphertext in JavaScript and use the remaining bytes as the actual ciphertext to be decrypted.
GCM is only defined for a nonce of 96 bit, so you might need to change it to NonceBitSize = 96 and read the first 12 bytes.
Based on this answer, you will need to slice the last 16 bytes of the ciphertext (MacBitSize = 128) for the authentication tag.
Example with a 96 bit nonce:
window.crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: encryptedArrayBuffer.slice(0, 12), //The initialization vector you used to encrypt
//additionalData: ArrayBuffer, //The addtionalData you used to encrypt (if any)
// tagLength: 128, //The tagLength you used to encrypt (if any)
tag: encryptedArrayBuffer.slice(-16), // authentication tag
},
cryptoKey, //from above
encryptedArrayBuffer.slice(12, -16) //ArrayBuffer of the data
// alternatively: encryptedArrayBuffer.slice(12) // in some cases leave the authentication tag in place
)

Javascript encryption in Crypto decryption in CryptoJS

I'm trying to encrypt server side (crypto Node) and decrypt client side (CryptoJS). I can create the key using cryptoJS, and can encrypt and decrypt when the same individual library is used however the issue is I cannot encrypt with Crypto but decrypt with CryptoJS, which is the real world scenario. There are no errors, just an empty response.
Any help greatly appreciated please!
iv = crypto.randomBytes(16),
orig = 'A confidential message.';
//Crypto JS creates key
var password = "sixteen byte key";
var salt = CryptoJS.lib.WordArray.random(128/8);
var key = CryptoJS.PBKDF2(password, salt, { keySize: 128 / 32, iterations: 1000 });
console.log("step1 generated key: "+ key);
//Convert key for crypto use - as a Buffer
var hashHex = key.toString(CryptoJS.enc.Hex);
var hash = new Buffer(hashHex,'hex');
//Test encryption and decryption with crypto (Node)
//use CryptoJS key to encrypt data using crypto cipheriv
var cipher2 = crypto.createCipheriv('aes-128-cbc', hash, iv); //iv must be a buffer
var encrypted1 = cipher2.update(orig, 'utf8', 'hex');
var encrypted2 = encrypted1 += cipher2.final('hex');
console.log("Crypto string:", encrypted2.toString());
// Start decrypt
var decipher = crypto.createDecipheriv('aes-128-cbc', hash, iv);
var dec = decipher.update(encrypted2, 'hex', 'utf8')
dec += decipher.final('utf8');
console.log("Crypto decrypted msg:", dec);
//test with crypto JS (ie the client)
//CryptoJS key is a string
var encryptedCJS = CryptoJS.AES.encrypt(orig, key.toString(), { iv: iv, mode: CryptoJS.mode.CBC});
console.log("CryptoJS encrypted: "+encryptedCJS);
var decryptedCryptoJS = CryptoJS.AES.decrypt(encryptedCJS, key.toString(), { mode: CryptoJS.mode.CBC, iv: iv });
console.log("CryptoJS decrypted msg: "+decryptedCryptoJS.toString(CryptoJS.enc.Utf8));
//This part does not work - use message encrypted by crypto but cannot decrypt with CryptoJS. decryptedCryptoJSFinal is empty
var decryptedCryptoJSFinal = CryptoJS.AES.decrypt(encrypted2, key.toString(), {iv: iv, mode: CryptoJS.mode.CBC});
console.log("FINAL CryptoJS decrypted: "+decryptedCryptoJSFinal.toString(CryptoJS.enc.Utf8));
I think the output of crypto encryption must be a different format to output of CryptoJS encryption but I cannot find the issue.
Overall I then intend to send the encrypted data as JSON for decryption on the client by CryptoJS.
I think your problem is in the client, if you pass the 'key' and the 'iv' as strings into 'CryptoJS.AES.encrypt', then CryptoJS takes your 'key' and a random 'salt' and generates a different secret key for the cipher. You can verify it generating different cipherTexts from the same clearText with the same key and iv, they will always be different, because a different secret key is generated inside CryptoJS each time you run the function.
To avoid this you need to pass the 'key' and 'iv' encoded (in 'hex' or 'base64' depending in the code you use) and then CryptoJS interprets that it doesn't have to generate a secret key and takes your 'key' to cipher.
Check this example:
//BACKEND with node crypto aes-256-cbc-> generate key and ciphertext
/////////////////////////////////////////////////////////////////////
var crypto = require('crypto');
var algorithm = 'aes-256-cbc';
var inputEncoding = 'utf8';
var outputEncoding = 'base64';
var pt = 'HELLO';
//generate key and iv
var masterKey = "253D3FB468A0E24677C28A624BE0F939";
var salt = "0000000000000000";
var keySize = 256/8;
var ivSize = 128/8;
var iterations = 100;
var outputKey = crypto.pbkdf2Sync(masterKey, salt, iterations, keySize+ivSize, "sha1");
// obtain key and IV splitting outputKey
var buffer = new Buffer(outputKey, inputEncoding);
var secretKey = buffer.slice(0, keySize);
var iv = buffer.slice(keySize, (keySize+ivSize));
console.log('secretKey->',secretKey.toString('base64'));
console.log('iv->',iv.toString('base64'));
//encrypt
var encrypt = crypto.createCipheriv(algorithm, secretKey, iv);
var encrypted = encrypt.update(pt, inputEncoding, outputEncoding);
encrypted += encrypt.final(outputEncoding);
console.log('Ciphering "%s"', pt);
//We obtain a
console.log('CipherText base64' string "%s ', encrypted.toString());
//FRONTEND with node CryptoJS aes-256-cbc-> generate same key and obtain cleartext
////////////////////////////////////////////////////////////////////
var masterKey = "253D3FB468A0E24677C28A624BE0F939";
var salt ="0000000000000000";
var iterations = 100;
var keySize = 256;
var ivSize = 128;
var outputKey = CryptoJS.PBKDF2(masterKey, salt, {
keySize: (keySize+ivSize)/32,
iterations: iterations
});
// the underlying words arrays might have more content than was asked: remove insignificant words
outputKey.clamp();
// split key and IV
var secretKey = CryptoJS.lib.WordArray.create(outputKey.words.slice(0,
keySize/32));
var iv = CryptoJS.lib.WordArray.create(outputKey.words.slice(keySize/32));
console.log('secretKey->', secretKey.toString(CryptoJS.enc.Base64));
console.log('iv->', iv.toString(CryptoJS.enc.Base64));
var decrypted = CryptoJS.AES.decrypt(ct, secretKey,{iv: iv});//Default mode CBC { mode: CryptoJS.mode.CFB });
console.log('CipherText->', ct);
console.log('ClearText decrypted', decrypted.toString(CryptoJS.enc.Utf8));

Categories

Resources