Search code examples
javascriptencryptioncryptojs

CryptoJS decrypt changes every time


I am using CryptoJS to manually decrypt a string with a provided set of values. The secret is provided and then an SHA256 has is taken of it. The message and initialization vector are base 64 encoded. Here's what I am trying, but every time I run it, the output changes - how can that be?! I'm at the end of my wits...

// Key and take the hash of it
var secretKey = 'TESTING123Secret_Key';
var secretKeyHash = CryptoJS.SHA256(secretKey).toString(CryptoJS.enc.Hex);

// Base 64 encoded values
var accountNumberBase64 = 'nxjYfo4Stw63YBEcnjo3oQ==';
var initializationVectorBase64 = 'HnNcvu9AP9yl09APWkWnDQ==';

// decode the values provided above
var accountNumberEncrypt = atob(accountNumberBase64);
var initializationVector = atob(initializationVectorBase64);

// Use crypto to decrypt
var decrypted = CryptoJS.AES.decrypt(
    {
        ciphertext: accountNumberEncrypt,
        salt: ''
    },
  secretKeyHash,
  {
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.NoPadding,
      iv: initializationVector,
      salt: ''
  }
);
 console.log('   decrypted, by hand: ' + decrypted.toString(CryptoJS.enc.Hex));

the last line changes every time this is run (run it on page load) - same values provided every time, output is different.

How it is supposed to work:

Decryption Instructions:
1. A static, secret key will be shared which will be used for decryption (Secret Key TBD).
    a. HASH the secret key with SHA256, encode it to Hex and use the first 32 characters. This will be used as the KEY when decrypting.
2. Two pieces of information will be sent via the POST method
    a. Parameter “AN”: A Base64 Encoded, AES-256-CBC Encrypted string which will represent the Account Number when decrypted
    b. Parameter “IV”: A Base64 Encoded initialization vector (IV) string which will be used in decrypting the Account Number string
3. Base64 Decode both parameters
4. Using the AES-256-CBC method, decrypt the encrypted string (which was base64 decoded as part of Step #3) with the initialization vector decoded in Step #3 and the hash created in Step #1a
5. The decryption should then provide you the account number.

Java code


Solution

  • There many issues with your code. It is hard to say what is really responsible for the non-deterministic decryption. I guess it is the fact that you're passing the key as a string which means that CryptoJS will assume that it is a password and try to use EVP_BytesToKey to derive a key from that. Since the salt is not set, CryptoJS probably has a bug that it generates a random salt for decryption (which it should not). You need to parse the key into a WordArray if you want to manually provide the key.

    The other main issue is using non-CryptoJS methods for decoding (atob) which means that you get some data format that cannot be directly read by CryptoJS. CryptoJS relies on the internal WordArray for representing all binary data or expects all strings to be UTF-8-encoded.

    Working code:

    // Key and take the hash of it
    var secretKey = 'TESTING123Secret_Key';
    var secretKeyHash = CryptoJS.SHA256(secretKey).toString(CryptoJS.enc.Hex).slice(0,32);
    secretKeyHash = CryptoJS.enc.Utf8.parse(secretKeyHash);
    
    // Base 64 encoded values
    var accountNumberBase64 = 'nxjYfo4Stw63YBEcnjo3oQ==';
    var initializationVectorBase64 = 'HnNcvu9AP9yl09APWkWnDQ==';
    
    var ct = CryptoJS.enc.Base64.parse(accountNumberBase64);
    var iv = CryptoJS.enc.Base64.parse(initializationVectorBase64);
    
    // Use crypto to decrypt
    var decrypted = CryptoJS.AES.decrypt({
        ciphertext: ct
      },
      secretKeyHash, {
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.NoPadding,
        iv: iv
      }
    );
    console.log('   decrypted, by hand: ' + decrypted.toString(CryptoJS.enc.Utf8));
    <script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
    <script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/sha256.js"></script>
    <script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/components/pad-nopadding-min.js"></script>