Search code examples
javascriptencryptioncryptographycryptojs

Decrypting AES hex message to complete mutual authentication with server


I am trying to follow these instructions provided by a reference document:

Variable A contains a sequence of 16-byte random numbers encrypted using Variable B.

You should decrypt it using Variable B and pads it to the end of Variable A.

The overall 32-byte Variable will be decrypted using Variable B and sent back to the server in order to have a successful authentication.

You should use AES128 CBC cipher mode for decrypting.

Variable A (16 bytes): e06e3e2ea024bba1185c33d64d2033b6
Variable B (16 bytes): 41435231323535552d4a312041757468

My attempt in CryptoJS

const message = 'e06e3e2ea024bba1185c33d64d2033b6';
const key = '41435231323535552d4a312041757468';

const decryptedMsg = CryptoJS.AES.decrypt(
  message,
  key,
  {
    mode: CryptoJS.mode.CBC,
  }
);

console.log(decryptedMsg.toString());

All this produces is an empty string. I don't have much experience in the way of encryption and would appreciate any assistance finding a solution so that I can build the finished 32-byte hexadecimal value and authenticate successfully.


Solution

    • The following applies to the parameters of the decrypt-method, here:

      1. parameter: ciphertext as CipherParams (if a string is passed here, it's Base64-decoded by default what is not intended here)
      2. parameter: key as WordArray (if a string is passed here, it's interpreted as a passphrase from which key and IV are derived what is not intended here)
      3. parameter: mode, initialization vector (IV) and padding

      Then you can decrypt as follows:

      var CryptoJS = require("crypto-js"); 
      
      function decrypt(hexCiphertext, hexKey, hexIV){    
          var cipherParams = CryptoJS.lib.CipherParams.create(
              {ciphertext: CryptoJS.enc.Hex.parse(hexCiphertext)}
          );
          var key = CryptoJS.enc.Hex.parse(hexKey);
          var iv = CryptoJS.enc.Hex.parse(hexIV);
          var decrypted = CryptoJS.AES.decrypt(
              cipherParams,
              key,
              {iv: iv, padding: CryptoJS.pad.NoPadding, mode: CryptoJS.mode.CBC}
          );
          return decrypted;
      }
      
    • The CBC mode uses an IV, which should be taken from the specification. The result depends on this IV, i.e. if you are using a wrong IV, the decryption will give a wrong result. If there is no IV in the specification, you can only guess and try e.g. a 0-vector on luck. The padding must also be taken from the specification. Since according to the description of the first part of the decryption, the encrypted and the decrypted message have the same length (namely 16 byte), obviously no padding (CryptoJS.pad.NoPadding) is used.

    • The following applies to the first part of the decryption (assuming a 0-vector for the IV):

      var hexMessage = 'e06e3e2ea024bba1185c33d64d2033b6';
      var hexKey = '41435231323535552d4a312041757468';
      var hexIV = '00000000000000000000000000000000';
      var decryptedStep1 = decrypt(hexMessage, hexKey, hexIV);
      var hexDecryptedStep1 = CryptoJS.enc.Hex.stringify(decryptedStep1)
      console.log(hexDecryptedStep1);
      

      with the following output:

      93fd6fa1ee388d326535abf7bd66310a
      

      and for the second part (assuming a 0-vector for the IV):

      var hexMessageStep2 = hexMessage + hexDecryptedStep1;
      var decryptedStep2 = decrypt(hexMessageStep2, hexKey, hexIV);
      var hexDecryptedStep2 = CryptoJS.enc.Hex.stringify(decryptedStep2);
      console.log(hexDecryptedStep2);
      

      with the following output:

      93fd6fa1ee388d326535abf7bd66310ac57db5bd524becba57445a9dbc536d0d