JavaScript AES Encryption showing wrong result against Java Android - javascript

I try to convert a string using AES method in JavaScript also I only need to use the CryptoJs library to achieve this.
When I try online from this website the result is coming as I expected which my Java program can decrypt it but when I try JavaScript I don't get the same result I get from the website.
This is what I need;
Data: {"test":1}
Secret: NdRgUkXp2s5v8y/A
The result should be: chib8X9Fnr7Vtn4VLRybKg==
The reason I'm referring the website, I don't know the mode and padding method of the above result but the below result is showing as expected from the server if you know the mode of the above result you can refer that also.
You can refer online encryption using https://www.devglan.com/online-tools/aes-encryption-decryption to validate.
This is what I get when I use JavaScript which is wrong;
var encrypted = CryptoJS.AES.encrypt('{"test",1}', "NdRgUkXp2s5v8y/A");
console.log(encrypted.toString())
Result: U2FsdGVkX1/GS5CecHJ10Z4qvSP8hY1NkDNtKGlg3OE=
Also it changes each time when generate.
I use this library https://cryptojs.gitbook.io/docs/

You pass the plain string value as a secret instead of UTF-8 encoded text and the value you get from the mentioned website is encrypted using PKCS7 padding method with ECB mode. Usually, this is the method most of the Android Java encryption methods support.
Encryption using CryptoJS.pad.Pkcs7 and make sure you provide the secret as UTF-8 as CryptoJS.enc.Utf8.parse(key) not in plain text.
let data = '{"test":1}'
let key = 'NdRgUkXp2s5v8y/A'
var encrypted = CryptoJS.AES.encrypt(data, CryptoJS.enc.Utf8.parse(key), {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
console.log(encrypted.toString());
Decryption method
var decrypted = CryptoJS.AES.decrypt(encrypted.toString(), CryptoJS.enc.Utf8.parse(key), {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
})
console.log(decrypted.toString(CryptoJS.enc.Utf8))
The below example proves the encrypted value matches as expected.
let data = '{"test":1}'
let key = 'NdRgUkXp2s5v8y/A'
/*
* Encryption
*/
var encrypted = CryptoJS.AES.encrypt(data, CryptoJS.enc.Utf8.parse(key), {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
console.log(encrypted.toString());
/*
* Decryption
*/
var decrypted = CryptoJS.AES.decrypt(encrypted.toString(), CryptoJS.enc.Utf8.parse(key), {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
})
console.log(decrypted.toString(CryptoJS.enc.Utf8) + "\n")
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

When you are printing out the decrypted string don't forget to add
.toString(CryptoJs.enc.utf8).
Check https://github.com/brix/crypto-js#plain-text-encryption

Related

AES CTR 128 Decryption with key and iv values using CryptoJs Javascript

I am trying to decrypt the below message which has been encrypted using AES CTR 128(The encryption is done in PHP need to decrypt in Javascript).
const message = "DEnw6dO5nEZ4k532f3I8U6Vy1m/kgeqZhPAUqjVwCDwBRTUcxkgSn95ROXdy3lPF7ybbIiDIUYtmfru6AaNvUlP9HxOdlMkjSJDMDz7pcDj+Hqioipp/hqfSqPfSkFI8Q+VkBV9b2zNy9lamy4NmaFBo03KvVm3Hw2cMWHeRgE8DTJYPoYrBqIlKJ3PT7E3edd433UGRC/wdZ1jkf1cgpD3HdMHDDRtLnA2q/mbmuYHs7hZSz+CLzBHP6iq4+jEO/6FrLCQSPXqEwyRvTQ73ngS1KOoI5LdhQV7s7vrKJcxHWEBX5A84F7VRo+at/0aE0m71GwmHvJSs5ZrLCkuiLKHqyzO4SUHNeP0f1VqQmuphC0o8M912xfquBI9tv0TIipbvw2hNKKL66tYCBSx8W0C6rodJ+yirHs+Q5CorBqzrYr28it3gqs0bHrkN7A=="
const key = "AJ89HFJHAJ89HFJH"
const iv = "hsjsjdkdueooqpjj"
CryptoJS.AES.decrypt(message, key, { iv: iv, mode: CryptoJS.mode.CTR })
console.log("Here is decrypted",decrypted.toString(CryptoJS.enc.Utf8))
After this I am getting below error message in console
Below is the PHP code which has been used
$encryption_text = openssl_encrypt($text, $crypto_details['ciphering'], $crypto_details['cryptokey'], $crypto_details['option'], $crypto_details['iv']);
Checked multiple resources but not able to find correct solution for this please guide me.
Thanks.

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().

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

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?

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

RSA Encryption with OAEP between Java and JavaScript

I am trying to encrypt a short string in JavaScript and decrypt it in Java. The decryption fails, and I think it's because of a difference in the block mode and/or padding between the two platforms. I tried encrypting the same string in both Java and JavaScript, and got different results, which indicates that there is indeed a difference. Here is the Java code that creates the key:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair keyPair = kpg.generateKeyPair();
And here is the Java code that I used to test the encryption:
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] bytes = cipher.doFinal("asdf".getBytes());
I send the public key across to the JavaScript process, and convert it to an ArrayBuffer, with the variable name publicKey. I have verified that the key on the JavaScript side matches the key on the Java side (by exporting it with crypto.subtle.exportKey and examining the bytes). Here is the JavaScript code that I used to test the encryption:
crypto.subtle.importKey('spki', publicKey,
{hash: 'SHA-256', name: 'RSA-OAEP'}, true,
['encrypt'])
.then((key) => {
crypto.subtle.encrypt({name: 'RSA-OAEP'}, key,
new TextEncoder().encode('asdf'))
.then((buffer) => {
});
});
The contents of the byte array in Java and the array buffer in JavaScript are not the same. The settings that I am unsure of are the parameter to Cipher#getInstance on the Java side and the parameters to importKey and encrypt on the JavaScript side. Are there any settings that will work between Java and JavaScript using the built-in classes? Or should I look at third-party libraries (e.g., Bouncy Castle)?
This is old, but here's an alternate solution if you want to use subtle crypto in javascript, and have control over the java decryption.
Here's how you decrypt in Java assuming you used the original JS code in the question to encrypt:
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
cipher.init(Cipher.DECRYPT_MODE, privKey, oaepParams);
byte[] decrypted = cipher.doFinal(encryptedBytes)
The issue with the Cipher RSA/ECB/OAEPWithSHA-256AndMGF1Padding is that it uses SHA-1 for the MGF1 Padding by default. Javascript uses SHA-256, which causes the mismatch. By specifying the MGF1ParamterSpec, we can force Java to use the same hashing algorithm as Javascript default.
In addition to #Chip's answer - which was really helpful - I would like to add the following case:
Assume you want to use the following for decryption in Javascript (webcrypto):
window.crypto.subtle.decrypt(
{
name: "RSA-OAEP",
hash: { name: "SHA-512" }
//label: Uint8Array([...]) //optional
},
privateRsaKey, //CryptoKey object containing private RSA key
encdata //ArrayBuffer containing to be decrypted data
)
.catch(function(err){
console.error(err);
})
Then you have to use the following OAEPParameterSpec for encryption in Java (and likely vice versa but I did not try that):
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-512", "MGF1",
new MGF1ParameterSpec("SHA-512"), PSource.PSpecified.DEFAULT);
Since #Chip referred to the MGF1 Padding I only, I assumed one would have to use
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-512", "MGF1",
new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
but apparently one has to change both hash functions to SHA-512 as shown in my first OAEPParameterSpec code block.
It looks as though the built-in encryption/decryption in JavaScript and Java do not have compatible settings for RSA encryption. A viable solution appears to be the forge library from github (forge on github). The key settings are described on the github page as follows (RSA examples):
// encrypt data with a public key using RSAES-OAEP/SHA-256/MGF1-SHA-1
// compatible with Java's RSA/ECB/OAEPWithSHA-256AndMGF1Padding
var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP', {
md: forge.md.sha256.create(),
mgf1: {
md: forge.md.sha1.create()
}
});

Categories

Resources