TripleDES encryption has different results in C# and JavaScript (CryptoJS) - javascript

I'm working on a project that uses an external API that is written in C#. In this API, I need to send some encrypted data while using their key that was provided when I started a session.
My project is built using NodeJS, so to do this encryption I am using the CryptoJS module. While talking with the API developer, he sent me a code showing how the encryption is made in C#. Here is the code with an example key and value to be encrypted.
TripleDESCryptoServiceProvider mDes = new TripleDESCryptoServiceProvider();
mDes.Key = Convert.FromBase64String("bv8czu/UPuZg6xNxnJAD/vRtbng9mQZX");
mDes.Mode = CipherMode.ECB;
mDes.Padding = PaddingMode.Zeros;
ICryptoTransform mDesEnc = mDes.CreateEncryptor();
byte[] data = Encoding.UTF8.GetBytes("1"); //value to encrypt
var crypto = Convert.ToBase64String(mDesEnc.TransformFinalBlock(data, 0, data.Length));
Console.WriteLine(crypto);
This results in the following encryption: 3EAaQjY2dgA=
As you can see in the code or running it, the encryption uses 3DES, mode ECB, Zero Padding and the key while in a byte array format has 24 bytes.
So I started to recreate this code in JavaScript and ended up with this:
var CryptoJS = require("crypto-js");
var encryptStringWith3DES = function(toEncrypt, key){
toEncrypt = "1";
key = "bv8czu/UPuZg6xNxnJAD/vRtbng9mQZX";
key = CryptoJS.enc.Utf8.parse(key);
console.log(toEncrypt + " " + key);
var encrypted = CryptoJS.TripleDES.encrypt(toEncrypt, key,{
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.ZeroPadding
}).toString();
console.log(encrypted);
}
And it results in the following encryption: dj1byXBV6ug=
I have searched for many answers and none worked. My suspicion is with how CryptoJS receives the key as a parameter and how the key and data are parsed.
Does any of you know how to make my JS code result in the same encrypted data as the C# one?

Related

Crypt in laravel Decrypt in react native Error

I am creating an app with reactnative and laravel,
so my code in laravel is creating a Qr code with a crypted data and send it to my front end (Reactjs),
When i try o decrypted in react native using Crypto js it adds a few caractere that i didn't add in my back end ,
I will show all function and exmeples ,
//Code in laravel crypt my QR info
$unix =time();
$unix_hashed = Crypt::encrypt($unix);
return QrCode::size(100)->generate($unix_hashed);
// Code in react native with cryptojs for the decrypt
var key = '6AhFLqwlExB9tn2Twql62EtbFDqBEv+S7tXW3h6a/0o=';
let encrypted = atob(data);
encrypted = JSON.parse(encrypted);
const iv = CryptoJS.enc.Base64.parse(encrypted.iv);
const value = encrypted.value;
key = CryptoJS.enc.Base64.parse(key);
var decrypted = CryptoJS.AES.decrypt(value, key, {
iv: iv
});
decrypted = decrypted.toString(CryptoJS.enc.Utf8);
// exemple of the output in console :
this is my output
but the problem it should be like this :
i:1634986874;
the " " & i & : & ; is extra
Can some one help me
Thanks
Crypt::encrypt() uses AES-256 in CBC mode by default, s. here.
Before encryption, the plaintext is serialized by calling the PHP function serialize() generating a storable representation of the value. For instance, serialize(1635015182) returns i:1635015182; and serialize('Hello world') returns s:11: "Hello world";. In the serialized data, i denotes an integer, s:11 denotes an 11 character string.
If decryption is performed with Crypt::decrypt(), unserialize() is called after decryption, which deserializes the data and restores the original plaintext.
When decrypting with another library, such as CryptoJS, the deserialization is not performed, so the serialized plaintexts are returned. Therefore, deserialization must be done manually here.
Fortunately, there is the locutus library that provides ports of PHP functionalities to JavaScript, so unserialize() can also be executed under JavaScript/NodeJS without much effort:
var CryptoJS = require('crypto-js');
var atob = require('atob');
var unserialize = require('locutus/php/var/unserialize')
// ciphertext from Crypt::encrypt()
var data = "eyJpdiI6IkYyaDhxenFJM0FjaW56TW83NkpuNHc9PSIsInZhbHVlIjoiNzhSeUs5em1WblpaRWk2OVBWVURnUT09IiwibWFjIjoiODg1NGFkOTk0MGVjMjIxODAyNzExOTgxYjhiMDM4YjliMmM5NzE5Mjc0YzFmNDVlMWU1MWRkMTQ2N2Q5ODdmNCJ9"
var key = '6AhFLqwlExB9tn2Twql62EtbFDqBEv+S7tXW3h6a/0o=';
let encrypted = atob(data);
encrypted = JSON.parse(encrypted);
const iv = CryptoJS.enc.Base64.parse(encrypted.iv);
const value = encrypted.value;
key = CryptoJS.enc.Base64.parse(key);
var decrypted = CryptoJS.AES.decrypt(value, key, {
iv: iv
});
decrypted = decrypted.toString(CryptoJS.enc.Utf8);
// unserialize
console.log(decrypted); // i:1635015182;
console.log(unserialize(decrypted)); // 1635015182
The unserialize() call in the last line restores the original plaintext.
Please note that encrypted.mac contains a MAC for checking data integrity. Here you can find a PHP implementation for Crypt::encrypt().

Different results trying to port SHA-1 digest from Python to browser JavaScript

Main question
I have the following short piece of legacy code that I am trying to port from Python (with just standard lib) to JavaScript - from the name of the methods I assume it creates a SHA-1 digest of the abc string
import hashlib
import hmac
print(hmac.new(b"abc", None, hashlib.sha1).hexdigest())
I searched for how to do that in the browser in JS and found the following code in the Mozilla documentation
var msgUint8 = new TextEncoder().encode('abc');
var hashBuffer = await crypto.subtle.digest('SHA-1', msgUint8);
var hashArray = Array.from(new Uint8Array(hashBuffer));
var hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
console.log(hashHex)
the problem is, they yield two completely different results, and I have no idea why:
cc47e3c0aa0c2984454476d061108c0b110177ae - Python
a9993e364706816aba3e25717850c26c9cd0d89d - JavaScript
I tried comparing the bytes of b"abc" with what new TextEncoder().encode('abc') returns and they are exactly the same: 0x61 0x62 0x63, so the problem lies somewhere else and I have no idea where.
I need the JavaScript code to return what the Python code returns. Any ideas?
Additionally
My final goal is to actually port this code (note the b"hello" instead of None):
print(hmac.new(b"abc", b"hello", hashlib.sha1).hexdigest())
so if you have an idea on that one too - I would hugely appreciate it!
The Python code calculates a SHA1 based HMAC. The JavaScript code on the other hand computes a SHA-1 hash. An HMAC needs a key in addition to the data, while a cryptographic hash function works without a key.
The first Python code uses the key abc and an empty message. The posted result for the HMAC is hex encoded:
cc47e3c0aa0c2984454476d061108c0b110177ae
The second Python code uses the same key and the message hello. The result for the HMAC is hex encoded:
d373670db3c99ebfa96060e993c340ccf6dd079e
The Java code determines the SHA-1 hash for abc. The result is
a9993e364706816aba3e25717850c26c9cd0d89d
So all results are correct, but are generated with different input data or algorithms.
The calculation of the HMAC can be implemented with the browser native WebCrypto-API as follows:
(async () => {
var hmac = await calcHMac('abc', 'hello');
console.log('HMAC: ', buf2hex(hmac));
var hmac = await calcHMac('abc', '');
console.log('HMAC: ', buf2hex(hmac));
})();
async function calcHMac(rawkey, data) {
var key = await window.crypto.subtle.importKey('raw', utf8Encode(rawkey), {name: 'HMAC', hash: 'SHA-1'},true, ['sign']);
var hmac = await window.crypto.subtle.sign('HMAC', key, utf8Encode(data));
return hmac;
}
function utf8Encode(str){
return new TextEncoder().encode(str);
}
function buf2hex(buffer) {
return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join(''); // from: https://stackoverflow.com/a/40031979/9014097
}
and provides the same result as the two Python codes.
A remark to SHA-1: Although HMAC/SHA-1 is considered to be secure (contrary to SHA-1), there are arguments to switch to SHA-256, see here.
The WebCrypto API is a bit cumbersome. A functionally identical implementation with CryptoJS, the library mentioned in the comment by Maurice Meyer, is simpler and looks like this:
var hmac = CryptoJS.HmacSHA1('hello', 'abc');
console.log('HMAC: ', hmac.toString(CryptoJS.enc.Hex));
var hmac = CryptoJS.HmacSHA1('', 'abc');
console.log('HMAC: ', hmac.toString(CryptoJS.enc.Hex));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
but requires CryptoJS as external dependency.

AES Encryption/Decryption in Node JS similar to Java

I am trying to replicate the Java code for AES Encryption and Decryption in Node JS.
Java Code
SecretKeySpec skeySpec;
String key = "a4e1112f45e84f785358bb86ba750f48";
public void encryptString(String key) throws Exception {
try {
skeySpec = new SecretKeySpec(key.getBytes(), "AES");
cipher = Cipher.getInstance("AES");
cipher.init(1, skeySpec);
byte encstr[] = cipher.doFinal(message.getBytes());
String encData = new String(encstr, "UTF-8");
System.out.println(encData);
} catch (NoSuchAlgorithmException nsae) {
throw new Exception("Invalid Java Version");
} catch (NoSuchPaddingException nse) {
throw new Exception("Invalid Key");
}
}
Node JS
var encryptKey = function (text) {
var cipher = crypto.createCipher('aes256', 'a4e1112f45e84f785358bb86ba750f48');
var crypted = cipher.update(text,'utf8', 'hex')
crypted += cipher.final('hex');
console.log(crypted);
return crypted;
}
I am unable to get the exact cipher-text in Node JS, which i am getting in Java.
Your code actually uses different encryption parameters in the 2 cases. AES, being a block cipher, takes: the plain text to encrypt, the initialization vector, also called IV (which is used in conjunction with the plaintext), and the encryption key.
In Java, the IV is, apparently, generated automatically on init() - from the Java SE platform docs for Cipher.init:
The generated parameters can be retrieved using getParameters or getIV
(if the parameter is an IV).
In Node.js, if using the deprecated createCipher function, the IV is generated automatically based on the provided key, probably in a different way than in Java, so you will get a different cipher text. However, you should be using the non-deprecated variant crypto.createCipheriv: https://nodejs.org/docs/latest-v12.x/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options
In order to exactly reproduce the cipher text, you should:
Use the same encryption algorithm on both sides - it's best to specify this exactly, for example aes-256-cbc, or an authenticated encryption scheme such as aes-256-gcm, which is harder to use but provides message authentication.
Use the same IV on both sides, by providing it in the initialization params in Java, and by using createCipheriv in Node; though beware, you should always randomize it in production! See https://stackoverflow.com/a/20888967/6098312
As a closing remark, when using block encryption, you'll usually be generating securely-random IVs, which means the ciphertexts will always differ from one another, even for the same plaintext. This is a good thing! It protects your payload from an attacker who observes the encrypted data and makes conclusions based on message repetitions.
Finally after reviewing Java Docs and Node JS Crypto Docs managed to get the result.
We have to use crypto.createCipheriv() instead of crypto.createCipher with a iv.
Here iv will be null.
Code :
let crypto = require('crypto');
var iv = new Buffer.from(''); //(null) iv
var algorithm = 'aes-256-ecb';
var password = 'a4e1112f45e84f785358bb86ba750f48'; //key password for cryptography
function encrypt(buffer){
var cipher = crypto.createCipheriv(algorithm,new Buffer(password),iv)
var crypted = Buffer.concat([cipher.update(buffer),cipher.final()]);
return crypted;
}
console.log(encrypt(new Buffer('TextToEncrypt')).toString())

Decrypt AES on server side c# encrypted on client side javascript using sjcl

I am trying to decrypt some text encrypted client-side and having a difficult time making this happen.
I am using the Standford Javascript Crypto Library (sjcl) on client side which generates a json string that I can send to the server, something like this...
sjcl.encrypt("my_key", "something, something");
and produces this json string...
"{"iv":"tfyF7oMXjauOkRnwaffU3A==","v":1,"iter":10000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"bq5G63+hk0U=","ct":"KahF3hQ3FZZ2l7APxLB4nRvw03Xt/qD04AChoA=="}"
Now I receive this json string server-side, and tried to do this...
dynamic data = JObject.Parse(json);
var key = Encoding.ASCII.GetBytes("my_key");
var iv = Convert.FromBase64String(data.iv.Value);
byte[] cypher = Convert.FromBase64String(data.ct.Value);
string plainText = null;
using (AesCryptoServiceProvider provider = new AesCryptoServiceProvider())
{
provider.Key = key;
provider.IV = iv;
ICryptoTransform decryptor = provider.CreateDecryptor(provider.Key, provider.IV);
using (MemoryStream ms = new MemoryStream(cypher))
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
plainText = sr.ReadToEnd();
}
}
}
}
return plainText;
The server side code above crashes on plainText = sr.ReadToEnd; and throws a "The input data is not a complete block". I know that AES requires the cypherText to have a block size of 128 and since the text varies in size, how do I guarantee that it has a block size of 128 and not screw up the intended value? Does setting the padding necessary here?
Also, what's the corresponding mode for "ccm" in the AESCryptoServiceProvider? Do the "iter" & "salt" need to be in play in order to decrypt correctly?
Thanks in advance!
Jaime

Generate a keyed hash value using the HMAC method with Google Apps Script

Is there a way to create a hash value in Google Apps Script? Google Apps Script will run server side code in the .gs code file. The .gs file is written in JavaScript. Because JavaScript is mostly a client side language, and encrypting anything client side isn't secure, maybe something like HMAC for Javascript isn't available? When I do a web search on hmac in javascript the first thing I get is crypto-js. But it looks like I need to link to some services in <script> tags:
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-md5.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha1.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha256.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha512.js"></script>
<script>
var hash = CryptoJS.HmacMD5("Message", "Secret Passphrase");
var hash = CryptoJS.HmacSHA1("Message", "Secret Passphrase");
var hash = CryptoJS.HmacSHA256("Message", "Secret Passphrase");
var hash = CryptoJS.HmacSHA512("Message", "Secret Passphrase");
</script>
The Secret Passphrase would be in your client side HTML. That doesn't make any sense! Oh! I just found some pseudocode in wikipedia.
Wikipedia HMAC pseudo Code
Here is my attempt at refactoring it:
//blocksize is the size in bytes and is set to 64 bytes.
//byte size of any UTF-8 string
function byteCount(s) {
return encodeURI(s).split(/%..|./).length - 1;
};
function hmac(key, message) {
var blocksize = 64;
var keyLngth = byteCount(key);
if (keyLngth > blocksize) {
key = hash(key); // keys longer than blocksize are shortened
}
else if (keyLngth < blocksize) {
key = key + [0x00 * (blocksize - keyLngth)]; // keys shorter than blocksize are zero-padded
};
var o_key_pad = [0x5c * blocksize] ⊕ key; // Where blocksize is that of the underlying hash function
var i_key_pad = [0x36 * blocksize] ⊕ key; // Where ⊕ is exclusive or (XOR)
return hash(o_key_pad + hash(i_key_pad + message));
};
I guess wherever the pseudo Code states: hash(key) one of the following hash functions: SHA-1, MD5, RIPEMD-128/160 needs to be used.
So I did a search on SHA-1 in JavaScript and found this:
http://www.movable-type.co.uk
Any info on how to create a HMAC value using Javascript would be greatly appreciated. I'll probably keep working on it, in the meantime. Even though it's Javascript, it's a Google .gs Apps Script code file, which runs on the server.
Apps Script has a built in Class Utility for creating HMAC Sha256 signature or token:
Official Apps Script Documentation HMAC Sha256 signature

Categories

Resources