AES 128 Decryption: First char of CryptoJS.enc.Latin1 is malformed - javascript

I have a string which is encoded and need to decrypt it. I have used the Crypto JS and after certain research, I could reach to below solution.
data = "+JdTb5BOloxaBHQlTw6NPLNV9lZix1OwhR3HF3IRtu2pdg/TLkrTw6Xu4JpKFlxE+zgOZavj0UynSZ+ojxmDXRbUlfyOc4YAncJVMXr28/AtfxZkNQoHbPIo7WxcSdidNE2k+DHZFcNNKOzYnvL1oDN4ezecs8Vo7K6vC5ZFLPUylXsi5sPsGye+TBbauPX+/wXa3hWUJVMNk6HUghW7l4N5Ei7HnrxLkFSFnz+9YUKYbFMEgV6wd9debHrpyytVhA3x2+Eyn5KnQ7iNJKQsNw==";
key = "062ec23950a55b9f8b21b0f9d45ca853";
// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
var iv = rawData.substring(0,32);
var crypttext = rawData.substring(32);
// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
{ ciphertext: CryptoJS.enc.Latin1.parse(crypttext) },
CryptoJS.enc.Hex.parse(key),
{ iv: CryptoJS.enc.Latin1.parse(iv) }
);
var test = CryptoJS.enc.Latin1.stringify(plaintextArray);
console.log(test.toString())
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/aes.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
The output of the above is
8«ÊWÒϬR¶jÛWY#ESPONSE_TEXT>
<RESULT>OK</RESULT>
<RESULT_CODE>-1</RESULT_CODE>
<TERMINATION_STATUS>SUCCESS</TERMINATION_STATUS>
<COUNTER>2</COUNTER>
</RESPONSE>
Instead of
<RESPONSE_TEXT>
<RESULT>OK</RESULT>
<RESULT_CODE>-1</RESULT_CODE>
<TERMINATION_STATUS>SUCCESS</TERMINATION_STATUS>
<COUNTER>2</COUNTER>
</RESPONSE>
I am not able to find why 8«ÊWÒϬR¶jÛWY is been coming to the result. Kindly help to find the missing thread.

Use - CryptoJS.lib.WordArray.random(128/8) to generate iv
data = "+JdTb5BOloxaBHQlTw6NPH2KFaA1mUL2vEuOqHmteozJutLfH1fQrDn+OE8Y1LgoFNqTjpuELCLB1noWbQ9Dz2g6NcD2vPAd4okiYArXnz+YKhVaGcqw+FTThjTWadMD/KvpzZ7SlxBh7HpCbZ/KMmxsOOnuk2F8pVEV3pkJMUgxV36c/iN9C+aRHPt95XRKzfU5ISaoBIq+/9K4WzW6i8yI8r6sdyRDoRc2q/EXuvX5666vdP1yojm2yhaL+1EwgkkYRn+GLyYuzFN1yCi+OA==";
key = "062ec23950a55b9f8b21b0f9d45ca853";
// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
var iv = CryptoJS.lib.WordArray.random(128/8);
var crypttext = rawData;
// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
{
ciphertext: CryptoJS.enc.Latin1.parse(crypttext)
},
CryptoJS.enc.Hex.parse(key),
{
iv: CryptoJS.enc.Latin1.parse(iv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
var decrypted = CryptoJS.enc.Latin1.stringify(plaintextArray);
console.log(decrypted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
Fiddle - https://jsfiddle.net/guptasanchit90dev/p00fhmp4/

Related

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

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>

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.

TripleDES encryption - c# and javascript differences

I have data encrypted in c#, and need to put together a demo of how to decrypt in javascript. (Note, this is just for a demo - we will not be putting keys into client side code!)
I cannot get the settings right using Crypto-js - I've tried lots of variations, but am getting nowhere.
I cannot change the c# code, so need to get the javascript to work the same way.
Current skeleton code is as follows -
C# (encrypt)
var EncryptionKey = Encoding.ASCII.GetBytes("14ggh11dd3fvv4n4aabb33a3");
var IV = Encoding.ASCII.GetBytes("312a44de");
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Key = EncryptionKey;
tdes.IV = IV;
byte[] buffer = Encoding.ASCII.GetBytes("test");
var ciphertext = Convert.ToBase64String(tdes.CreateEncryptor().TransformFinalBlock(buffer, 0, buffer.Length));
Console.WriteLine(HttpUtility.UrlEncode(ciphertext));
which generates the cipher VrB1Ih0Ll%2fQ%3d
javascript (decrypt)
function decryptByDESModeCBC(ciphertext) {
var key = '14ggh11dd3fvv4n4aabb33a3';
var iv = '312a44de'
ciphertext = decodeURIComponent(ciphertext);
var keyBytes = CryptoJS.enc.Utf8.parse(key);
var ivBytes = CryptoJS.enc.Utf8.parse(iv);
var decrypted = CryptoJS.DES.decrypt({
ciphertext: CryptoJS.enc.Base64.parse(ciphertext)
}, keyBytes, {
iv:ivBytes,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
});
return decrypted.toString(CryptoJS.enc.Utf8);
}
function test()
{
console.log(decryptByDESModeCBC("VrB1Ih0Ll%2fQ%3d"));
}
Expected result is "test", but I am getting blank.
Any pointers would be great.
As answered by #Topaco in the comments
I needed to use CryptoJS.TripleDES instead of CryptoJS.DES

Incorrect decryption output when using CryptoJS library for AES

I am trying to encrypt and decrypt some random data using AES. I can successfully encrypt data using the following code,
function padString(source) {
var paddingChar = 'x';
var size = 16;
var padLength = size - source.length;
for (var i = 0; i < padLength; i++) source += paddingChar;
return source;
}
var key = CryptoJS.enc.Hex.parse('dassdadsasad');
var iv = CryptoJS.enc.Hex.parse('fedcba9876543210');
var message = "0x72648174091c3f7cd41773f636ca9a15756798";
var padMsg = padString(message);
var encrypted = CryptoJS.AES.encrypt(padMsg, key, {
iv: iv,
padding: CryptoJS.pad.NoPadding,
mode: CryptoJS.mode.CBC
});
but when I try to decrypt it,
var decrypted = CryptoJS.AES.decrypt(encrypted, key, {
iv: iv,
padding: CryptoJS.pad.NoPadding,
mode: CryptoJS.mode.CBC
});
I get the incorrect output for decryption instead of getting back my plain text.
Here is the output for instance,
Message: "0x72648174091c3f7cd41773f636ca9a15756798" 40
Padded message: "0x72648174091c3f7cd41773f636ca9a15756798" 40
Encrypted: 8qCjEtSLhchErbhJu8jo/xy8T5T1eSAFazuWLKwKdAW9F0ZUjJLlZw==
Encrypted text: f2a0a312d48b85c844adb849bbc8e8ff1cbc4f94f57920056b3b962cac0a7405bd1746548c92e567
Decrypted c748c55c0212d1688e79de5f00379eb0d802789501e6cbab3e6255b902eaa528a40d32123bcd0ce1
Can someone please tell me what am I doing wrong or if I am missing something ?
Here is the live demonstration, https://jsfiddle.net/4zb9hrxb/267/
Your key is invalid:
var key = CryptoJS.enc.Hex.parse('dassdadsasad');
does not make much sense in the first place since s does not exist in hexadecimal notation. Either specify a 256 bit key as hex, like this:
key = CryptoJS.enc.Hex.parse("123456789012345678901234567890ab");
(note: only hexadecimal characters) or let CryptoJS derive the key from a passphrase:
var key = 'dassdadsasad';
either follow what phihag said, or you can use some key derivation function such as, PBKDF2 to derive key from passphrase (as phihag suggested).
consider this for example,
import pbkdf2,
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/pbkdf2.js"></script>
and generate key as follows,
var passphrase = "dassdadsasad"
var keySize = 256;
var iterations = 100;
var salt = CryptoJS.lib.WordArray.random(128/8);
var key = CryptoJS.PBKDF2(passphrase, salt, {
keySize: keySize/32,
iterations: iterations
});
there is a very good example available here,
http://www.adonespitogo.com/articles/encrypting-data-with-cryptojs-aes/

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