3
votes

I need to decrypt incoming requests encrypted with AES, I try to use shared example and unable to find right set of parameters

  • Encryption:AES/CBC/PKCS5Padding AES/CBC/PKCS5
  • Initialization vector: emptybyte array which length is 16
  • Test secret key:1234567890123456
  • Plain text: abcdefghigklmnopqrstuvwxyz0123456789
  • Encrypted: 8Z3dZzqn05FmiuBLowExK0CAbs4TY2GorC2dDPVlsn/tP+VuJGePqIMv1uSaVErr

I use next

const cryptkey = '1234567890123456';
const cleardata = "abcdefghigklmnopqrstuvwxyz0123456789";
const crypted = "8Z3dZzqn05FmiuBLowExK0CAbs4TY2GorC2dDPVlsn/tP+VuJGePqIMv1uSaVErr";

var decrypt = CryptoJS.AES.decrypt(crypted, cryptkey, {
    iv: CryptoJS.enc.Hex.parse('0000000000000000'),
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
});
var ddd = decrypt.toString(CryptoJS.enc.Utf8);
console.log(ddd);

Every time I am getting empty string. Where do I fail?

------ UPDATE -----

New version with applied comments, still does not work

const cryptkey = CryptoJS.enc.Utf8.parse('1234567890123456');
const crypted = CryptoJS.enc.Base64.parse("8Z3dZzqn05FmiuBLowExK0CAbs4TY2GorC2dDPVlsn/tP+VuJGePqIMv1uSaVErr");

var decrypt = CryptoJS.AES.decrypt(crypted, cryptkey, {
    iv: CryptoJS.enc.Hex.parse('00000000000000000000000000000000'),
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
});
2

2 Answers

10
votes

You would have to parse the UTF-8 key first:

const cryptkey = CryptoJS.enc.Utf8.parse('1234567890123456');

If you don't do that CryptoJS will assume it is a password and derive the actual key from that.

As Maarten also pointed out...

The ciphertext also must be decoded from Base64:

const crypted = CryptoJS.enc.Base64.parse("8Z3dZzqn05FmiuBLowExK0CAbs4TY2GorC2dDPVlsn/tP+VuJGePqIMv1uSaVErr");

Note that the decryptor expects a CipherParams object, which you can simulate by passing {ciphertext: crypted} to the decrypt function. Alternatively, you can rely on CryptoJS to decode the ciphertext from Base64, you pass in that string as-is.

The IV must be 16 bytes long for AES-CBC which are 32 characters if encoded as Hex:

CryptoJS.enc.Hex.parse('00000000000000000000000000000000')

Examples

const cryptkey = CryptoJS.enc.Utf8.parse('1234567890123456');
const crypted = CryptoJS.enc.Base64.parse("8Z3dZzqn05FmiuBLowExK0CAbs4TY2GorC2dDPVlsn/tP+VuJGePqIMv1uSaVErr");

var decrypt = CryptoJS.AES.decrypt({ciphertext: crypted}, cryptkey, {
    iv: CryptoJS.enc.Hex.parse('00000000000000000000000000000000'),
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
});

console.log(decrypt.toString(CryptoJS.enc.Utf8));
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>

As Matt correctly noted, CryptoJS can do the ciphertext decoding for you if the ciphertext is encoded as Base64:

const cryptkey = CryptoJS.enc.Utf8.parse('1234567890123456');
const crypted = "8Z3dZzqn05FmiuBLowExK0CAbs4TY2GorC2dDPVlsn/tP+VuJGePqIMv1uSaVErr";

var decrypt = CryptoJS.AES.decrypt(crypted, cryptkey, {
    iv: CryptoJS.enc.Hex.parse('00000000000000000000000000000000'),
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
});

console.log(decrypt.toString(CryptoJS.enc.Utf8));
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>

Security considerations:

The IV must be unpredictable (read: random). Don't use a static IV, because that makes the cipher deterministic and therefore not semantically secure. An attacker who observes ciphertexts can determine when the same message prefix was sent before. The IV is not secret, so you can send it along with the ciphertext. Usually, it is simply prepended to the ciphertext and sliced off before decryption.

A key should be randomly chosen from all possible bytes, because a key consisting of ASCII characters is much easier to brute-force than a key consisting of all available bytes.

It is better to authenticate your ciphertexts so that attacks like a padding oracle attack are not possible. This can be done with authenticated modes like GCM or EAX, or with an encrypt-then-MAC scheme.

If you're using only symmetric encryption you need the exact same key at the server and the client. If you send the encryption key from the server to the client or the other way around you need to encrypt your symmetric encryption key. The easiest way to do this would be to use TLS. If you use TLS, then the data as well as key are encrypted, so you don't need to encrypt it yourself. This doesn't provide any security, just a little bit of obfuscation. You should read: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful/

0
votes

You are forgetting to base 64 decode the ciphertext in crypted (I guess you'd have to use atob() to do that). Your IV is too small as well, hexadecimals take two hex digits per byte.