Search code examples
javascriptencryptioncryptojs

CryptoJS decrypts text files correctly but not images


The following code takes a file from user and encrypts it. The user later can retrieve the same file and decrypt it. The problem is; this code works well with the text-based files (.txt, .c, .py, etc) and the output of the decryption is the same as the input of the encryption. However, when I use an .PNG image, the output is corrupted and the image is not showing.

I tried checking the image code with a text editor and I can see a difference in encoding, but I'm not sure at which stage it was produced. Any help would be appreciated. BTW - I tried also jpeg files but it's the same issue.

crypto-js output after decryption

Encryption Code:

function uploadFile() {

    var file = fileupload.files[0];
    var reader = new FileReader();
    var key = document.getElementById("enc_key").value; //to get encryption key
    var fileEnc = null;
    reader.onload = function() {
        var wordArray = CryptoJS.lib.WordArray.create(reader.result);
        console.log("before: " + wordArray);
        var encrypted = CryptoJS.AES.encrypt(wordArray, key);
        downloadStringAsFile("encr", encrypted);
        console.log("after [from encryption]: " + encrypted);
        var fileEnc = new Blob([encrypted]);
        encryptAndUpload(fileEnc);
     
reader.readAsArrayBuffer(file); 
}

Decryption Code:

async function decryptAndDownload_Unzipped(index) {

  // Some code to get the file_url from the server...

  https.get(file_url,  function(res) {
    var data = [], dataLen = 0; 
  
    res.on('data', function(chunk) {
      data.push(chunk);
      dataLen += chunk.length;
    }).on('end', function() {
      var buf = Buffer.alloc(dataLen);
      for (var i = 0, len = data.length, pos = 0; i < len; i++) { 
        data[i].copy(buf, pos); 
        pos += data[i].length; 
      } 

      dec_file = "";
      for(var i = 0; i < data[0].length; i++){
        dec_file += String.fromCharCode(data[0][i]);
      }

      var decrypted = (CryptoJS.AES.decrypt(dec_file, key)).toString(CryptoJS.enc.Latin1);

      var sampleBytes = new Int8Array(8192);
      var saveByteArray = (function () {
          var a = document.createElement("a");
          document.body.appendChild(a);
          a.style = "display: none";
          return function (decrypted, name) {
              var blob = new Blob(decrypted, {type: "octet/stream"}),
                  url = window.URL.createObjectURL(blob);
              a.href = url;
              a.download = name;
              a.click();
              window.URL.revokeObjectURL(url);
          };
      }());
      saveByteArray([decrypted], 'example.png');

      
    });
  });


}

Solution

  • The issue was here:

    var decrypted = (CryptoJS.AES.decrypt(dec_file, key)).toString(CryptoJS.enc.Latin1);

    There was no need to convert the decryption resutls to string. And I also needed to convert the output to Uint8Array type.

    I used this function from [this question][1]:

        var arrayOfWords = wordArray.hasOwnProperty("words") ? wordArray.words : [];
        var length = wordArray.hasOwnProperty("sigBytes") ? wordArray.sigBytes : arrayOfWords.length * 4;
        var uInt8Array = new Uint8Array(length), index=0, word, i;
        for (i=0; i<length; i++) {
            word = arrayOfWords[i];
            uInt8Array[index++] = word >> 24;
            uInt8Array[index++] = (word >> 16) & 0xff;
            uInt8Array[index++] = (word >> 8) & 0xff;
            uInt8Array[index++] = word & 0xff;
        }
        return uInt8Array;
    }```
    
    Then I converted the decryption results to Uint8Array then downloaded the file and it works perfectly.
    
    ```var decrypted = CryptoJS.AES.decrypt(dec_file, key);```
    ```var dec_results = convertWordArrayToUint8Array(decrypted);```
    
    
      [1]: https://stackoverflow.com/questions/60520526/aes-encryption-and-decryption-of-files-using-crypto-js