javascripttypescriptaescryptojsecb

cryptojs.decrypt returns empty result


I need to decrypt a hex message in JavaScript that has the exact same outcome as the code written in Java. However the Javascript version using CryptoJs returns an empty result

Code in Java:

private static void create()
{
    byte[] sessionKey = fromHexString("dae25b4defd646cd99b7b95d450d6646");
    byte[] data = fromHexString("2700012e27999bdaa6b0530375be269985a0238e5e4baf1528ebaf34a8e5e8c13a58b25bcb82514ee6c86c02ff77ac52bdbd88");

    byte[] payload_data = new byte[48];
    byte[] decrypted_data = new byte[48];

    for(int i=0;i<48;i++) {
        payload_data[i]= data[3+i];
    }


    try{
        SecretKeySpec skeySpec = new SecretKeySpec(sessionKey, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        decrypted_data = cipher.doFinal(payload_data);

    }catch(Exception e){
        System.out.println(e);
    }

    String my_data = byteArrayToHex(decrypted_data);
    System.out.println(my_data);
}

private static String byteArrayToHex(byte[] a) {
    StringBuilder sb = new StringBuilder(a.length * 2);
    for(byte b: a)
        sb.append(String.format("%02X", b));
    return sb.toString();
}

private static byte[] fromHexString(String src) {
    byte[] biBytes = new BigInteger("10" + src.replaceAll("\\s", ""), 16).toByteArray();
    return Arrays.copyOfRange(biBytes, 1, biBytes.length);
}

which returns a result of: "248A8143837F51E03C3522934DD47C38612C90EC57D79D7DE6174EAC85B75F9ADCD7D6686EBF4B9F2E9FE441D373E69E"

Code in JavaScript:

import * as cryptojs from 'crypto-js';

export function create() {
  const sessionKey = Buffer.from('dae25b4defd646cd99b7b95d450d6646', 'hex');
  const data = Buffer.from('2700012e27999bdaa6b0530375be269985a0238e5e4baf1528ebaf34a8e5e8c13a58b25bcb82514ee6c86c02ff77ac52bdbd88', 'hex');

  const payloadData = Buffer.alloc(48);

  for (let i = 0; i < 48; i += 1) {
    payloadData[i] = data[3 + i];
  }

  const decrypted = cryptojs.AES.decrypt(
    cryptojs.enc.Hex.parse(toHexString(payloadData)),
    cryptojs.enc.Hex.parse(toHexString(sessionKey)),
    {
      mode: cryptojs.mode.ECB,
      padding: cryptojs.pad.NoPadding,
    }
  ).toString(cryptojs.enc.Hex);

  console.log({
    decrypted,
  });
}

function toHexString(byteArray) {
  // eslint-disable-next-line no-bitwise
  return Array.prototype.map.call(byteArray, byte => `0${(byte & 0xff).toString(16)}`.slice(-2)).join('');
}

result:

{ decrypted: '' }

Any idea on what might be wrong ?


Solution

  • The decryption with CryptoJS could look as follows:

    function decrypt() {
        var sessionKey = 'dae25b4defd646cd99b7b95d450d6646';
        var data = '2700012e27999bdaa6b0530375be269985a0238e5e4baf1528ebaf34a8e5e8c13a58b25bcb82514ee6c86c02ff77ac52bdbd88';
        var payload_data = data.substr(6);
        var decrypted = CryptoJS.AES.decrypt(
            payload_data,
            CryptoJS.enc.Hex.parse(sessionKey),
            {
                format: CryptoJS.format.Hex,
                mode: CryptoJS.mode.ECB,
                padding: CryptoJS.pad.NoPadding,
            }
        ).toString(CryptoJS.enc.Hex);
        console.log(decrypted.replace(/(.{48})/g,'$1\n'));
    }
    
    decrypt();
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

    Update: Regarding your comment: The issue in your code is that the ciphertext is passed as WordArray. The following two changes are one way to make it work:

    ...
    const decrypted = cryptojs.AES.decrypt(
        toHexString(payloadData),                           // pass the ciphertext as hex encoded string
        cryptojs.enc.Hex.parse(toHexString(sessionKey)),
        {
            format: cryptojs.format.Hex,                    // specify the encoding of the ciphertext
            mode: cryptojs.mode.ECB,
            ...
    

    cryptojs.AES.decrypt() expects the ciphertext in a CipherParams object (and not simply in a WordArray). Alternatively the ciphertext can be passed Base64 encoded or in another encoding that must be specified explicitly with the format parameter (e.g. hexadecimal here, Base64 is the default). The ciphertext is then implicitly converted into a CipherParams object, see here.

    But please consider: Since all conversions can be done with CryptoJS onboard means, helpers like toHexString() are not really necessary. For this there are especially the encoder classes, see here. The same applies to the NodeJS Buffer. It makes more sense to work with WordArrays, because they are processed directly by CryptoJS.