Search code examples
typescriptcryptojs

CrytoJS AES decrypt returning a blank value - Typescript


I have the following Typescript code and have tried a few things but I am unable to get the decryption to work. Would appreciate some insight.

Thanks in advance!

import {Injectable} from '@angular/core';
import * as CryptoJS from 'crypto-js';
@Injectable({
  providedIn: 'root'
})
export class UmCryptoService {
  constructor() {
  }
  encrypt<T>(key, value: T): string{
    key = CryptoJS.enc.Utf8.parse(key);
    const i = CryptoJS.enc.Utf8.parse('1583288699248111');
    const ciphertext = CryptoJS.AES.encrypt(JSON.stringify(value), key, {iv: i}).toString();
    console.log('SS cipherText: ' + ciphertext);
    return ciphertext;
  }
  decrypt(key, value){
    key = CryptoJS.enc.Utf8.parse(key);
    const i = CryptoJS.enc.Utf8.parse('1583288699248111');
    const decryptedData = CryptoJS.AES.decrypt(CryptoJS.enc.Base64.parse(value), key, {iv: i});
    console.log('SS utf8 decryptedData: ' + decryptedData.toString(CryptoJS.enc.Utf8));
    return decryptedData.toString(CryptoJS.enc.Utf8);
  }
}

In the console, I see:

LOG: 'SS cipherText: UbNlnYr0qd7ua0TcPTMIyxItPNvNscCfQYIP244Mt/5e7vPHZ1cFDWE9XgV7pA6a'
LOG: 'SS utf8 decryptedData: '

Here are a couple unit tests:

it('should encrypt string value', () => {
    const key = '1234567890987654321';
    const strValue = 'Hey check out this encryption!';
    const cipher = service.encrypt(key, strValue);
    console.log('SS: cipher str: ' + cipher);
    expect(cipher).toBeDefined();
    );
  });

fit('should decrypt cipher', fakeAsync (() => {
    const key = '1234567890987654321';
    const cipher = 'UbNlnYr0qd7ua0TcPTMIyxItPNvNscCfQYIP244Mt/5e7vPHZ1cFDWE9XgV7pA6a';
    const strValue = service.decrypt(key, cipher);
    tick(100000);
    console.log('SS: strValue: ' + strValue);
    expect(strValue).toBeDefined();
  }));

Solution

  • The ciphertext must be passed to CryptoJS.AES.decrypt() as CipherParams object or as Base64 encoded string (s. The Cipher Input) and not as WordArray as in the actual code (assuming that value in decrypt() is the return value of encrypt()), i.e. a possible fix is:

    const decryptedData = CryptoJS.AES.decrypt(value, key, {iv: i}); 
    

    Complete code:

    function encrypt(key, value){
        key = CryptoJS.enc.Utf8.parse(key);
        const i = CryptoJS.enc.Utf8.parse('1583288699248111');
        const ciphertext = CryptoJS.AES.encrypt(JSON.stringify(value), key, {iv: i}).toString();
        //console.log('SS cipherText: ' + ciphertext);
        document.getElementById("ct").innerHTML = 'SS cipherText: ' + ciphertext;
        return ciphertext;
    }
    
    function decrypt(key, value){
        key = CryptoJS.enc.Utf8.parse(key);
        const i = CryptoJS.enc.Utf8.parse('1583288699248111');
        const decryptedData = CryptoJS.AES.decrypt(value, key, {iv: i}); // Fix: pass Base64 encoded ciphertext
        //console.log('SS utf8 decryptedData: ' + decryptedData.toString(CryptoJS.enc.Utf8));
        document.getElementById("dt").innerHTML = 'SS utf8 decryptedData: ' + decryptedData.toString(CryptoJS.enc.Utf8);
        return decryptedData.toString(CryptoJS.enc.Utf8);
    }
    
    var key = '01234567890123456789012345678901';
    var ct = encrypt(key, {test: 'The quick brown fox jumps over the lazy dog'});
    var dt = decrypt(key, ct);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
    <p style="font-family:'Courier New', monospace;" id="ct"></p>
    <p style="font-family:'Courier New', monospace;" id="dt"></p>


    Note that the posted unit test is unsuitable for testing the code snippet in question, since in the unit test key is passed as string and therefore the built-in key derivation is used, while in the code snippet key is passed as WordArray and therefore the key is applied directly (s. The Cipher Input). So the underlying logics are very different.

    Also, a static IV is insecure. The (non-secret) IV should be randomly generated for each encryption and passed to the decrypting side together with the ciphertext - usually concatenated.