How to Decrypt Message With Cryptojs Aes. I Have a Working Ruby Example

How to decrypt message with CryptoJS AES. I have a working Ruby example

This seems to work for me (writes decoded string to the console):

data = "IYkyGxYaNgHpnZWgwILMalVFmLWFgTCHCZL9263NOcfSo5lBjAzOZAtF5bF++R0Bi+9c9E+p3VEr/xvj4oABtRWVJ2wlWzLbYC2rKFk5iapFhb7uZCUpO4w4Su3a5QFa2vInjYueziRoqySZd/DpstMJ8rsJ94VGizFFFZ1l0sw1ax+wfBAv5+wHs/hlnHi/ea66KBO3rgXKahvV28h+4bh5etc8RCrmiiNbfg6Oj0jQJDjdYIdW8T9YPOI9E1hih8lbfRnMWcOFJgYekfLpoy5LI525UGnlM46J1k6ekLqsn9FqvbiOOoLgqa4YqBm1i9P0ePyjkME+t+RiL8xXX+ItgOYr9G7kM64wlTJPCW8B/crmUdmGzQNC/hD/u/8wfHBS2f8u6OtQMG/+Kpk1oju8lcUZGI/4S8A6/OuktvQr2zgnbs2aADMrM37Oait/pJ3G73S7NwVT8EaK+X43c0C/fUvW2/bD/rqCNpAh9WQlz4Cj6JHwjbmwuind6aCimF1tHjXuR9FXu+g17sPT4ZkKZ6aeBG+m170XdCGn2hVM0wH1rh3VeCG2u/JFqfuGKGSoqeHeNY/icu9pEhtZDzHd7aPoaMXcWvXC9PjooBf7GM1EPacSdnon1kBobjtKSt1l15DjO5TMrJoX7VO7GotQwo+uI/u5Kop01hBXxyxyggl1/8N0ESohPJoqLDrIwvbGK5kW4B49FVPnx9CMvjZDdSsoxPAh+hx6SPe8Hj0Nx4bRs06cbtOkte/V8QSYIqjiJDleEqPrdiKlvgToZz9L29ZR/3Ln65qU1sq7q9c0SEYxIopV7TdTjFS7y76zDPFZkhzc3DjfLtJo/M1hdtt648APcZdmAIgWH6fh3eJZ0qbiPh8RStYH7I2COmnlMw4+t/B5mlhYVSgwPK2Ir736Mh+P9Bw0fF8r9Ghhs4AJzpU0RiK9d1tCsrLfK/hSRwTXhtsSB6eDWEGkO7oeEIz43mgn0sv3SrA9JNHzYkg=";
key = "2e35f242a46d67eeb74aabc37d5e5d05";

// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
var iv = btoa(rawData.substring(0,16));
var crypttext = btoa(rawData.substring(16));

// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
{
ciphertext: CryptoJS.enc.Base64.parse(crypttext),
salt: ""
},
CryptoJS.enc.Hex.parse(key),
{ iv: CryptoJS.enc.Base64.parse(iv) }
);

// Convert hex string to ASCII.
// See https://stackoverflow.com/questions/11889329/word-array-to-string
function hex2a(hex) {
var str = '';
for (var i = 0; i < hex.length; i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
return str;
}

console.log(hex2a(plaintextArray.toString()));

The important bits are to specify no salt in your input and pass the key as a WordArray as jlvaquero says. There are probably some efficiency improvements to be made, e.g. I'm guessing there's an easier way to convert a string to a WordArray than going to base64 and back, but at least it successfully decodes. I lifted the hex2a function from StackOverflow question Word Array to String.

Edit: I figured out how to convert strings to WordArrays and vice-versa with CryptoJS so the extra base64 encode/decode and hex-to-ASCII function are not needed. Also it turns out the salt property need not be specified. This is more compact and efficient:

data = "IYkyGxYaNgHpnZWgwILMalVFmLWFgTCHCZL9263NOcfSo5lBjAzOZAtF5bF++R0Bi+9c9E+p3VEr/xvj4oABtRWVJ2wlWzLbYC2rKFk5iapFhb7uZCUpO4w4Su3a5QFa2vInjYueziRoqySZd/DpstMJ8rsJ94VGizFFFZ1l0sw1ax+wfBAv5+wHs/hlnHi/ea66KBO3rgXKahvV28h+4bh5etc8RCrmiiNbfg6Oj0jQJDjdYIdW8T9YPOI9E1hih8lbfRnMWcOFJgYekfLpoy5LI525UGnlM46J1k6ekLqsn9FqvbiOOoLgqa4YqBm1i9P0ePyjkME+t+RiL8xXX+ItgOYr9G7kM64wlTJPCW8B/crmUdmGzQNC/hD/u/8wfHBS2f8u6OtQMG/+Kpk1oju8lcUZGI/4S8A6/OuktvQr2zgnbs2aADMrM37Oait/pJ3G73S7NwVT8EaK+X43c0C/fUvW2/bD/rqCNpAh9WQlz4Cj6JHwjbmwuind6aCimF1tHjXuR9FXu+g17sPT4ZkKZ6aeBG+m170XdCGn2hVM0wH1rh3VeCG2u/JFqfuGKGSoqeHeNY/icu9pEhtZDzHd7aPoaMXcWvXC9PjooBf7GM1EPacSdnon1kBobjtKSt1l15DjO5TMrJoX7VO7GotQwo+uI/u5Kop01hBXxyxyggl1/8N0ESohPJoqLDrIwvbGK5kW4B49FVPnx9CMvjZDdSsoxPAh+hx6SPe8Hj0Nx4bRs06cbtOkte/V8QSYIqjiJDleEqPrdiKlvgToZz9L29ZR/3Ln65qU1sq7q9c0SEYxIopV7TdTjFS7y76zDPFZkhzc3DjfLtJo/M1hdtt648APcZdmAIgWH6fh3eJZ0qbiPh8RStYH7I2COmnlMw4+t/B5mlhYVSgwPK2Ir736Mh+P9Bw0fF8r9Ghhs4AJzpU0RiK9d1tCsrLfK/hSRwTXhtsSB6eDWEGkO7oeEIz43mgn0sv3SrA9JNHzYkg=";
key = "2e35f242a46d67eeb74aabc37d5e5d05";

// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
var iv = rawData.substring(0,16);
var crypttext = rawData.substring(16);

// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
{ ciphertext: CryptoJS.enc.Latin1.parse(crypttext) },
CryptoJS.enc.Hex.parse(key),
{ iv: CryptoJS.enc.Latin1.parse(iv) }
);

console.log(CryptoJS.enc.Latin1.stringify(plaintextArray));

How to decrypt message with CryptoJS AES? I have a working node crypto example

Thanks to @GrafiCode pointing me to the right place, I was able to solve it using format property of config object.

Following is the code:

const crypto = require('crypto-js');
const encryptedData = 'b6ab428efbcb93c2f483178114ac0608530e54428f1378c6d3be108531b730d1888e562044cd3acb8844a04d9d7602d83b96f0a758248ffd07cd9c530b76c91c';

const decryptResponse = (data) => {
const key = crypto.enc.Utf8.parse('F5:A4:F4:AB:BF:68:CF:86:51:B4:AA');
const iv = crypto.enc.Hex.parse(data.substring(0, 32));
const payload = data.substring(32);
let decryptedData = crypto.AES.decrypt(
payload,
key,
{
iv: iv,
mode: crypto.mode.CBC,
// padding: crypto.pad.NoPadding,
format: crypto.format.Hex
});
console.log(crypto.enc.Utf8.stringify(decryptedData));
}

decryptResponse(encryptedData);

I commented out the padding: crypto.pad.NoPadding as there were non-printable characters at the end of the decryptedData when it was enabled.

CryptoJS is not working with IV and key, but it works in Ruby

The problem is that Ruby expects the key to be in binary format, not hex. So you need to do this:

#!/usr/bin/env ruby

require 'openssl'
require 'base64'

data = "When in Rome do as the Romans do"
key = "2e35f242a46d67eeb74aabc37d5e5d05"
aes = OpenSSL::Cipher.new("aes-128-cbc")
aes.encrypt()
aes.key = key.scan(/../).collect{|x| x.hex}.pack("c*")
iv_value = aes.random_iv
aes.iv = iv_value
crypt = aes.update(data) + aes.final()
crypt_string = (Base64.encode64(iv_value + crypt))
puts crypt_string

For me that prints

mdnLCY6MdwEONY1AxR/vjVKMssB+yrPsz4QMjfl6fDXxv68E0EUxtAqa4VUo
fTkjq2Hqyd48UV3dyWmEbwXw5Q==

If I put that into your HTML file (without changing any code), I get back the original message:

<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/core-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>

<script>
data = "mdnLCY6MdwEONY1AxR/vjVKMssB+yrPsz4QMjfl6fDXxv68E0EUxtAqa4VUo\nfTkjq2Hqyd48UV3dyWmEbwXw5Q==\n";
key = "2e35f242a46d67eeb74aabc37d5e5d05";

var rawData = atob(data);
var iv = rawData.substring(0,16);
var crypttext = rawData.substring(16);

// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
{ ciphertext: CryptoJS.enc.Latin1.parse(crypttext) },
CryptoJS.enc.Hex.parse(key),
{ iv: CryptoJS.enc.Latin1.parse(iv) }
);

console.log(plaintextArray);
console.log(CryptoJS.enc.Latin1.stringify(plaintextArray));
</script>
</head>
<body>
</body>
</html>

So the problem is that your Ruby code produced a gibberish cyphertext. Fix the key and re-encrypt, and the JS should start working.

How to decrypt of AES-256-GCM created with ruby in sjcl.js

AES-GCM is an authenticated encryption algorithm. It automatically generates an authentication tag during encryption, which is used for authentication during decryption. This tag is not considered in the current Ruby code. It is 16 bytes by default, can be retrieved with cipher.auth_tag and must be added, e.g.:

ciphertext = cipher.update(plaintext) + cipher.final + cipher.auth_tag 

Regarding nonce/IV, note that Base64 encoding should actually be done after concatenation (which, however, is not critical for a 12 bytes nonce/IV commonly used with GCM).


On the JavaScript side the separation of the nonce/IV is missing. Ciphertext and tag do not need to be separated because the sjcl processes the concatenation of both (ciphertext|tag):

const GCM_NONCE_LENGTH = 12 * 8
const GCM_TAG_LENGTH = 16 * 8

// Separate IV and ciptertext/tag combination
let ivCiphertextTagB64 = "2wLsVLuOJFX1pfwwjoLhQrW7f/86AefyZ7FwJEhJVIpU+iG2EITzushCpDRxgqK2cwVYvfNt7KFZ39obMMmIqhrDCIeifzs="
let ivCiphertextTag = sjcl.codec.base64.toBits(ivCiphertextTagB64)
let iv = sjcl.bitArray.bitSlice(ivCiphertextTag, 0, GCM_NONCE_LENGTH)
let ciphertextTag = sjcl.bitArray.bitSlice(ivCiphertextTag, GCM_NONCE_LENGTH)

// Derive key via SHA256
let key = sjcl.hash.sha256.hash("my password")

// Decrypt
let cipher = new sjcl.cipher.aes(key)
let plaintext = sjcl.mode.gcm.decrypt(cipher, ciphertextTag, iv, null, GCM_TAG_LENGTH)
//let plaintext = sjcl.mode.gcm.decrypt(cipher, ciphertextTag, iv) // works also; here the defaults for the AAD ([]) and the tag size (16 bytes) are applied
console.log(sjcl.codec.utf8String.fromBits(plaintext))
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sjcl/1.0.8/sjcl.min.js "></script>

Trying to print hex string with CryptoJS

CryptoJS.AES.encrypt() returns a CipherParams object that encapsulates several data, including the ciphertext as WordArray (s. here). By default, .toString() returns the hex encoded data for a WordArray: