CryptoJS javascript AES-128, ECB encrypt / decrypt
Asked Answered
C

1

5

Goal: Simple CryptoJS example to encrypt, decrypt using AES-128, ECB, 0-padding.

See below my runnable sample.

Input text is "US0378331005-USD-US-en" which is encrypted (hopefully AES-128 with all the above) and then decrypted (which does not work)

text: US0378331005-USD-US-en
key: 11A1764225B11AA1
key length: 16
encrypted 951422f8ac8354acf23fbc
decrypted a5126fa0fc3cb4c39a4f3e637be98a73

var text = 'US0378331005-USD-US-en';
var key = '11A1764225B11AA1'; // to ensure AES-128 this has to be 16 bit

console.log('text:', text);
console.log('key:', key);
console.log('key length:', key.length );

text = CryptoJS.enc.Hex.parse(text);
key = CryptoJS.enc.Hex.parse(key);
var iv = CryptoJS.enc.Hex.parse("00000000000000000000000000000000");

var encrypted = CryptoJS.AES.encrypt(text, key, { iv: iv, mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding });
encrypted = encrypted.ciphertext.toString(CryptoJS.enc.Hex);

console.log('encrypted', encrypted);

var decrypted =  CryptoJS.AES.decrypt(encrypted, key, {iv: iv, mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding });

console.log('decrypted', decrypted.toString() );
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/aes.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/md5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/enc-hex.min.js"></script>
Childish answered 10/6, 2022 at 7:9 Comment(0)
M
10

There are several bugs in your code:

  • Block cipher modes like ECB always require padding if the size of the plaintext does not correspond to an integer multiple of the blocksize, as is the case in the above example. Actually, an exception should be thrown. CryptoJS, however, implicitly pads with 0x00 values, performs the encryption, and truncates the ciphertext to the length of the original plaintext. This processing is haphazard and is (im my opionion) even a bug.
    As fix, padding must be applied. Since you are talking about 0-padding, you may mean zero padding, i.e. CryptoJS.pad.ZeroPadding. Note that PKCS#7 padding (the default) is more reliable.
  • You are using the hex encoder for the key import, so the 16 hex digits of your key are converted to an 8 bytes key, which is not a valid AES key (valid AES key lengths are 16, 24 and 32 bytes). However, due to a bug, CryptoJS processes this key size but produces a non-AES compliant result.
    As fix, for instance, a 32 hex digit key must be used for AES-128 in combination with the Hex encoder. Another alternative that works with the posted key is the Utf8 encoder.
  • The plaintext is obviously not hex encoded, so no hex encoder may be applied.
    As fix the Utf8 encoder is to be used.
  • ECB mode does not use an IV, i.e. the IV is ignored and can therefore be omitted. Note that the ECB mode is insecure precisely because it does not use an IV. Therefore, a mode with an IV should be applied.
  • During decryption CryptoJS.AES.decrypt() expects a CipherParams object, but in the current code the hex encoded ciphertext is passed.
    The fix is to either create a CipherParams object from the hex encoded ciphertext and pass it, or to pass the Base64 encoded ciphertext, which CryptoJS implicitly converts to a CipherParams object.
  • The Utf8 decoding of the decrypted data is missing.

Fixed code:

var text = 'US0378331005-USD-US-en';
var key = '11A1764225B11AA1'; 

console.log('text:', text);
console.log('key:', key);
console.log('key length:', key.length );

// Fix: Use the Utf8 encoder
text = CryptoJS.enc.Utf8.parse(text); 
// Fix: Use the Utf8 encoder (or apply in combination with the hex encoder a 32 hex digit key for AES-128)
key = CryptoJS.enc.Utf8.parse(key); 

// Fix: Apply padding (e.g. Zero padding). Note that PKCS#7 padding is more reliable and that ECB is insecure
var encrypted = CryptoJS.AES.encrypt(text, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding }); 
encrypted = encrypted.ciphertext.toString(CryptoJS.enc.Hex);
console.log('encrypted', encrypted);

// Fix: Pass a CipherParams object (or the Base64 encoded ciphertext)
var decrypted =  CryptoJS.AES.decrypt({ciphertext: CryptoJS.enc.Hex.parse(encrypted)}, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding }); 

// Fix: Utf8 decode the decrypted data
console.log('decrypted', decrypted.toString(CryptoJS.enc.Utf8)); 
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
Malachite answered 10/6, 2022 at 8:10 Comment(1)
Hats off to you sir, what a great, detailed answer to my problem. Thank you very much!Childish

© 2022 - 2024 — McMap. All rights reserved.