How to match Key & IV generated by C# and JS CryptoJS - javascript

I need help to find a way how to match IV and KEY from C# and JS,
with a simple C# code:
Rijndael rijndael = Rijndael.Create();
byte[] saltArray = Encoding.ASCII.GetBytes("20190925");
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes("password", saltArray, 1000);
rijndael.Key = pdb.GetBytes(32);
rijndael.IV = pdb.GetBytes(16);
Console.WriteLine(BitConverter.ToString(rijndael.Key).Replace("-","").ToLower());
//Output rijndael.Key = c1b34ea814586db4a22dad37e11c7256322ab0eee3a14ed1898f93d7a264242f
Console.WriteLine(BitConverter.ToString(rijndael.IV).Replace("-","").ToLower());
//Output rijndael.IV = 063ead20a9d5f35ab83e1156ebe7c099
with a CryptoJS i can get a same KEY as C# but with the IV i don't get it why the value is not match
CryptoJS code:
let key = CryptoJS.PBKDF2('password', '20190925', {keySize:256/32, iterations:1000})
console.log('key', key.toString(CryptoJS.enc.Hex))
//Output key = 'key', 'c1b34ea814586db4a22dad37e11c7256322ab0eee3a14ed1898f93d7a264242f'
let iv = CryptoJS.PBKDF2('password', '20190925', {keySize:128/32, iterations:1000})
console.log('iv', iv.toString(CryptoJS.enc.Hex))
//Output iv = 'iv', 'c1b34ea814586db4a22dad37e11c7256'
I just think if rijndael.IV = pdb.GetBytes(16); will just get half of rijndael.Key = pdb.GetBytes(32); hex, but the value totally different
is there a way to match IV of C# using CryptoJS?

PBKDF2 must be executed for a length equal to the sum of key length and IV length. The result is divided into two parts. The first part corresponds to the key, the second part to the IV:
let keyLen = 256/32
let ivLen = 128/32
let keyiv = CryptoJS.PBKDF2('password', '20190925', {keySize:keyLen + ivLen, iterations:1000})
let key = CryptoJS.lib.WordArray.create(keyiv.words.slice(0, keyLen));
let iv = CryptoJS.lib.WordArray.create(keyiv.words.slice(keyLen, keyLen + ivLen));
console.log('keyiv:', keyiv.toString())
console.log('key :', key.toString())
console.log('iv :', iv.toString())
This results in:
keyiv: c1b34ea814586db4a22dad37e11c7256322ab0eee3a14ed1898f93d7a264242f063ead20a9d5f35ab83e1156ebe7c099
key : c1b34ea814586db4a22dad37e11c7256322ab0eee3a14ed1898f93d7a264242f
iv : 063ead20a9d5f35ab83e1156ebe7c099
which matches the result of the C# code.

Related

How to format data to correctly decrypt hex strings in CryptoJS

I have a TCP server that is receiving information via direct IP. I am receiving this information encrypted in AES-128-CBC.(hex byte buffer) I then turn the buffer into a string of hex bytes with no spaces. "originalMsg"
I get passed the IV, the encrypted message, and I have the key hardcoded. I still can't get the message to decrypt correctly. I will usually get a completely different decryption or an empty string. I am assuming one or more of my variables is in the wrong format?
I have zero experience with cryptology before this week but this is my task. So, I apologize if the problem is something miniscule.
I am receiving the data from my colleague from C language. I have a fear that CryptoJS isn't compatible with the data I am being passed? Any help is much appreciated.
code.js
const CryptoJS = require("crypto-js");
var originalMsg = "5303F15FB8317A010300000000000001F3E0C003E24340E4E4"
var encrypted = "00000003C4302B119E7BB9C36655F6BCDF251808C6748A11949A89309AD17600F6164FF45CDC"
var key = "242E389B1672B4ECEA92FE7466DF3A52"
var iv = "0000E34500FF0000000000FF00000000"
var decryptData2 = function(encryptedData) {
var C = CryptoJS;
var Key = CryptoJS.enc.Hex.parse("242E389B1672B4ECEA92FE7466DF3A52")
var IV = CryptoJS.enc.Hex.parse("0000E34500FF0000000000FF00000000")
// I have tried
var decryptedText = C.AES.decrypt(encryptedData, Key, {
iv: IV,
mode: C.mode.CBC,
padding: C.pad.Pkcs7
});
return decryptedText.toString(CryptoJS.enc.Utf8);
}
var result = decryptData2(CryptoJS.enc.Hex.parse("00000003C4302B119E7BB9C36655F6BCDF251808C6748A11949A89309AD17600F6164FF45CDC"))
console.log(result);
Edit:
Updated iv and encrypted variables
//old vars
encrypted ="00000003C4302B119E7BB9C36655F6BCDF251808C6748A11949A89309AD17600F6164FF45CDC"
iv = "02C4100000E34500FF0000000000FF00"
It's always good to rely on sample data because that helps a lot in finding a working solution. Below you find a sample program that is been able to decrypt the message.
Some remarks how it works:
a) I'm converting all given data to Crypto-JS word arrays to be compatible with the cryptography methods:
var key = CryptoJS.enc.Hex.parse("242E389B1672B4ECEA92FE7466DF3A52");
b) to see what encrypted data I should use for decryption I'm first encrypting the originalMsg and get this output:
ciphertext: 2b119e7bb9c36655f6bcdf251808c6748a11949a89309ad17600f6164ff45cdc
c.txt exp: 00000003c4302b119e7bb9c36655f6bcdf251808c6748a11949a89309ad17600f6164ff45cdc
The first line is the encrypted "originalMsg", the second line is the data you identified as "encrypted" - see the difference ? As I earlier commented the encrypted value is 6 bytes too long (it has to be multiples of 16, your "encrypted" is 38 bytes long).
c) trying to decrypt needs an input in base64 encoding (there are other ways, this way is the most convenient way for me to use it here) so first I'm encoding the word array "encrypted" to (Base64 encoded) "encryptedBase64" and present the string to the aesCbcDecrypt function. As the data is too long there is no output.
var encryptedBase64 = CryptoJS.enc.Base64.stringify(encrypted);
var decrypted = aesCbcDecrypt(key, iv, encryptedBase64);
console.log("decrypted: " + decrypted);
result:
decrypted:
d) now I'm cutting off the first 6 bytes (12 hex "characters") from encrypted and use the remaining data for the same decryption function:
var encrypted2 = CryptoJS.enc.Hex.parse("2B119E7BB9C36655F6BCDF251808C6748A11949A89309AD17600F6164FF45CDC");
console.log("encrypted2 length: " + encrypted2.sigBytes);
var encrypted2Base64 = CryptoJS.enc.Base64.stringify(encrypted2);
var decrypted2 = aesCbcDecrypt(key, iv, encrypted2Base64);
console.log("decrypted2: " + decrypted2);
console.log("orig.Msg : " + originalMsgHex);
result:
decrypted2: 5303f15fb8317a010300000000000001f3e0c003e24340e4e4
orig.Msg : 5303F15FB8317A010300000000000001F3E0C003E24340E4E4
The decrypted2 value is now equal to the originalMsg.
If you like to see the code running in an online compiler - here is the link: https://repl.it/#javacrypto/SoCryptoJsDecrypt#index.js
I leave it up to you to put this information together to get a programmatically solution as this should only explain how to get the dercrypted data.
complete output:
key length: 16
iv length: 16
ciphertext: 2b119e7bb9c36655f6bcdf251808c6748a11949a89309ad17600f6164ff45cdc
c.txt exp: 00000003c4302b119e7bb9c36655f6bcdf251808c6748a11949a89309ad17600f6164ff45cdc
encrypted length: 38
decrypted:
encrypted2 length: 32
decrypted2: 5303f15fb8317a010300000000000001f3e0c003e24340e4e4
orig.Msg : 5303F15FB8317A010300000000000001F3E0C003E24340E4E4
Security warning: the code does have no exception handling and is for educational purpose only.
complete code:
const CryptoJS = require("crypto-js");
var originalMsgHex = "5303F15FB8317A010300000000000001F3E0C003E24340E4E4";
var originalMsg = CryptoJS.enc.Hex.parse(originalMsgHex);//("5303F15FB8317A010300000000000001F3E0C003E24340E4E4");
var encrypted = CryptoJS.enc.Hex.parse("00000003C4302B119E7BB9C36655F6BCDF251808C6748A11949A89309AD17600F6164FF45CDC");
var key = CryptoJS.enc.Hex.parse("242E389B1672B4ECEA92FE7466DF3A52");
var iv = CryptoJS.enc.Hex.parse("0000E34500FF0000000000FF00000000");
console.log("key length: " + key.sigBytes);
console.log("iv length: " + iv.sigBytes);
// encryption of originalMsg
var ciphertext = aesCbcEncrypt(key, iv, originalMsg);
console.log("ciphertext: " + ciphertext);
console.log("c.txt exp: " + encrypted);
// decryption of encryption fails due to wrong length (not multiple of 16)
console.log("encrypted length: " + encrypted.sigBytes); // result: 38
// prepare encrypted for decryption by base64encoding
var encryptedBase64 = CryptoJS.enc.Base64.stringify(encrypted);
var decrypted = aesCbcDecrypt(key, iv, encryptedBase64);
console.log("decrypted: " + decrypted);
// cutting off the first 6 bytes from encrypted
var encrypted2 = CryptoJS.enc.Hex.parse("2B119E7BB9C36655F6BCDF251808C6748A11949A89309AD17600F6164FF45CDC");
console.log("encrypted2 length: " + encrypted2.sigBytes);
var encrypted2Base64 = CryptoJS.enc.Base64.stringify(encrypted2);
var decrypted2 = aesCbcDecrypt(key, iv, encrypted2Base64);
console.log("decrypted2: " + decrypted2);
console.log("orig.Msg : " + originalMsgHex);
function aesCbcEncrypt(keyF, ivF, data) {
const cipher = CryptoJS.AES.encrypt(data, keyF,
{
iv: ivF,
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
});
return cipher.ciphertext;
}
function aesCbcDecrypt(keyF, ivF, ciphertext) {
const cipher = CryptoJS.AES.decrypt(ciphertext, keyF,
{
iv: ivF,
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
});
return cipher;
}

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.

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));

Node.js Crypto input/output types

I am trying to figure out the Node.js Crypto library and how to use it properly for my situation.
My Goal is:
key in hex string 3132333435363738313233343536373831323334353637383132333435363738
text in hex string 46303030303030303030303030303030
ciphered text in hex string 70ab7387a6a94098510bf0a6d972aabe
I am testing this through a c implementation of AES 256 and through a website at http://www.hanewin.net/encrypt/aes/aes-test.htm
This is what I have to far, it's not working the way I would expect it to work. My best guess is that the input and output types are incorrect for the cipher function. The only one that works is utf8 if I use hex it fails with a v8 error. Any ideas on what I should convert or change to get it to work.
var keytext = "3132333435363738313233343536373831323334353637383132333435363738";
var key = new Buffer(keytext, 'hex');
var crypto = require("crypto")
var cipher = crypto.createCipher('aes-256-cbc',key,'hex');
var decipher = crypto.createDecipher('aes-256-cbc',key,'hex');
var text = "46303030303030303030303030303030";
var buff = new Buffer(text, 'hex');
console.log(buff)
var crypted = cipher.update(buff,'hex','hex')
The output in crypted in this example is 8cfdcda0a4ea07795945541e4d8c7e35 which is not what I would expect.
Your code is using aes-256-cbc when the website you are deriving test vectors from is using ecb mode. Also, you are calling createCipher, but with ECB you should use createCipheriv with no IV (see nodeJS: can't get crypto module to give me the right AES cipher outcome),
Here is some code that demonstrates this:
var crypto = require("crypto");
var testVector = { plaintext : "46303030303030303030303030303030",
iv : "",
key : "3132333435363738313233343536373831323334353637383132333435363738",
ciphertext : "70ab7387a6a94098510bf0a6d972aabe"};
var key = new Buffer(testVector.key, "hex");
var text = new Buffer(testVector.plaintext, "hex");
var cipher = crypto.createCipheriv("aes-256-ecb", key, testVector.iv);
var crypted = cipher.update(text,'hex','hex');
crypted += cipher.final("hex");
console.log("> " + crypted);
console.log("? " + testVector.ciphertext);
The output of running that code is not exactly what I expect, but the first block of the encrypted output matches your expectation. Probably another parameter that needs to be tweaked.:
$ node test-aes-ecb.js
> 70ab7387a6a94098510bf0a6d972aabeeebbdaed7324ec4bc70d1c0343337233
? 70ab7387a6a94098510bf0a6d972aabe

Categories

Resources