10

I've been trying to decrypt an ArrayBuffer object using CryptoJS, but so far it always returns a blank WordArray. The files (images) are encrypted in an iOS and Android app, sent to a server, and downloaded in this web app to be decrypted and displayed. The iOS and Android apps are able to decrypt the files without problems, so there's nothing wrong with the encryption process.

The files are downloaded with an XMLHttpRequest with responseType set to arraybuffer. Here's my code so far:

// Decrypt a Base64 encrypted string (this works perfectly)
String.prototype.aesDecrypt = function(key) {

    var nkey = CryptoJS.enc.Hex.parse(key.sha256());
    return CryptoJS.AES.decrypt(this.toString(), nkey, {
        iv: CryptoJS.enc.Hex.parse('00000000000000000000000000000000'),
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    }).toString(CryptoJS.enc.Utf8);

}

// Decrypt a plain encrypted ArrayBuffer (this is the problem, it always outputs an empty WordArray)
ArrayBuffer.prototype.aesDecrypt = function(key) {

    // Get key
    if (!key) return null;
    var nkey = CryptoJS.enc.Hex.parse(key.sha256());

    // Get input (if I pass the ArrayBuffer directly to the create function, it returns
    // a WordList with sigBytes set to NaN)
    //var input = CryptoJS.lib.WordArray.create(this);
    var input = CryptoJS.lib.WordArray.create(new Uint8Array(this));

    // Decrypt
    var output = CryptoJS.AES.decrypt(input, nkey, {
        iv: CryptoJS.enc.Hex.parse('00000000000000000000000000000000'),
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });

    // Output is an empty WordList
    console.log("Output: ", output);

}

Another question I have is how do you convert a WordArray to an ArrayBuffer?

jjv360
  • 4,120
  • 3
  • 23
  • 37

2 Answers2

15

The conversion of ArrayBuffer -> WordArray has been discussed in CryptoJS's issue 46. For that reason a TypedWordArraywhere you can also pass an ArrayBuffer has been added.


To use that additionally include the following script:

<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/components/lib-typedarrays.js"></script>

Then you can simply do:

var wordArray = CryptoJS.lib.WordArray.create(arrayBuffer);

/* perform decryption of `wordArray` */

To reconvert the resulting decryptedWordArray to an ArrayBuffer, the simplest approach would probably be, to first convert it to a Base64-String (as discussed here) and then decode that String to the desired ArrayBuffer (see here). The whole procedure would look something like this:

dcWordArray = ... // your decrypted WordArray
dcBase64String = dcWordArray.toString(CryptoJS.enc.Base64); // to Base64-String
dcArrayBuffer = base64DecToArr(dcBase64String).buffer; // to ArrayBuffer

Edit:

For a more efficient conversion (no intermediate Base64String necessary) check out Aletheios answer to that question (the function wordToByteArray(wordArray) and then do .buffer).

Community
  • 1
  • 1
i_turo
  • 2,679
  • 1
  • 13
  • 15
  • If I provide the `ArrayBuffer` directly to `WordArray.create`, `sigBytes` is NaN. If I wrap it in a `Uint8Array` it's quite a large number. Is the sigBytes field important? Either way, `AES.decrypt` returns an empty WordArray... Also, when converting back to an `ArrayBuffer` won't a Base64 string be too big? The files can be up to 30MB that are being decrypted... – jjv360 Sep 02 '14 at 08:23
  • `sigBytes` is only important if you are using a `signature/MAC`, which looking at your working *String-encryption-example* you don't. If your files are that large, that could really get a problem, I'll try to find a better solution. Could you maybe provide some test-data (encrypted `ArrayBuffer` (a small one if possible) + `key` + `IV`), so I can try out decryption myself. Thanks. – i_turo Sep 02 '14 at 08:36
  • 1
    Sorry, was completely wrong with `sigBytes`. They tell how many bytes are saved in the `WordArray`. The mustn't be `NaN`. But actually they aren't for me when testing it locally. (when you include `lib-typedarrays.js`) – i_turo Sep 02 '14 at 09:47
  • I made a simple JSFiddle [here](http://jsfiddle.net/jqfr2c99/1/), hopefully it helps... The ArrayBuffer created there is just a simple text file (48 bytes) and I showed decrypting it as a Base64 string which works, and as an ArrayBuffer, which doesn't... – jjv360 Sep 02 '14 at 09:52
  • It seems that `CryptoJS` for some reason does only **decrypt** `Base64`-Strings. (see also [this](http://stackoverflow.com/questions/20519166/cant-decrypt-string-with-cryptojs) similar problem) Possibly the easiest solution would be `ArrayBuffer` -> `String` -> `decrypt()` -> `ArrayBuffer`. I'll update my answer with that info later :) – i_turo Sep 02 '14 at 11:21
  • Ok I've got it mostly working now, still have to convert original ArrayBuffer to Base64 for decryption unfortunately. It works now even for large files (I tried a 36MB file), even if it takes a lot of RAM to do it... – jjv360 Sep 05 '14 at 09:23
  • Have anyone submitted a bug on that? This seems like a major performance killer to me. – Moshe Kravchik Sep 28 '14 at 09:59
  • @i_turo what is `base64DecToArr()`? –  Jan 06 '17 at 00:47
-2

you can use the web crypto API to directly decrypt arrayBuffer. No need to convert binary data to a string, which is so inefficient, and it could cause memory and CPU rises.