I have a code snippet that was written for the aesjs library (https://github.com/ricmoo/aes-js/blob/master/index.js). A server is sent a request with an AES encrypted token based on static Base64 strings (data, data key, iv), with the current timestamp added to the data. Here is the code to use the aesjs library:
function base64ToByteArray(base64String){try{var sliceSize=1024;var byteCharacters=window.atob(base64String);var bytesLength=byteCharacters.length;var slicesCount=Math.ceil(bytesLength/sliceSize);var byteArrays=new Array(slicesCount);for(var sliceIndex=0;sliceIndex<slicesCount;++sliceIndex){var begin=sliceIndex*sliceSize;var end=Math.min(begin+sliceSize,bytesLength);var bytes=new Array(end-begin);for(var offset=begin,i=0;offset<end;++i,++offset){bytes[i]=byteCharacters[offset].charCodeAt(0);}byteArrays[sliceIndex]=new Uint8Array(bytes);}return byteArrays;}catch(e){api.info("Couldn't convert to byte array: "+e);return undefined;}}
var msg = 'ZuwnEFQ7gTbtfEH5rz9ZOh/zV2LplPscMl2qLnV9gOU';
var dataKey = 'REXGk/8fpOQSoCXWIGZk2g';
var delimiter = '|';
var iv = 'KCgWOBhGET1aIRMiFcIw';
iv = new Uint8Array(aesjs.utils.utf8.toBytes(atob(iv)));
// Set timestamp
timestamp = getTimestamp(); // returns string of format: 2019-12-23T05:11:35Z
// Start encrypting!
var data1 = msg + delimiter + timestamp;
var data_bytes1 = aesjs.utils.utf8.toBytes(data1);
var data_bytes_padded1 = aesjs.padding.pkcs7.pad(data_bytes1);
var dKeyStr = base64ToByteArray(dataKey)[0];
aesCbc = new aesjs.ModeOfOperation.cbc(dKeyStr, iv);
encryptedBytes1 = aesCbc.encrypt(data_bytes_padded1);
var encryptedString1 = btoa(String.fromCharCode.apply(null, encryptedBytes1));
// LvmLYbp0a1M4XUdPWXO5y3Ntn/+UMqibzrIrfGG5Ctlbf2zI+YGF6ipp+TAdEZkMrBwbl/AnWHA32c43slNgs+673ar3MsT7HWgZLhQVftg=
The code for CryptoJS has to be a little different, of course, but I'm getting a wrong encryptedString1 (the server complains about it). Can anyone tell me what I'm doing wrong? CryptoJS code:
function byteArrayToWordArray(ba){var wa=[],i;for(i=0;i<ba.length;i++){wa[(i/4)|0]|=ba[i]<<(24-8*i);}return CryptoJS.lib.WordArray.create(wa,ba.length);}
var msg = 'ZuwnEFQ7gTbtfEH5rz9ZOh/zV2LplPscMl2qLnV9gOU';
var dataKey = 'REXGk/8fpOQSoCXWIGZk2g';
var delimiter = '|';
var iv = 'KCgWOBhGET1aIRMiFcIw';
iv = CryptoJS.enc.Base64.parse(iv);
// Set timestamp
timestamp = getTimestamp(); // returns string of format: 2019-12-23T05:11:35Z
// Start encrypting!
data1 = givenReqId + delimiter + timestamp;
dataKey = CryptoJS.enc.Base64.parse(dataKey);
encrypted1 = CryptoJS.AES.encrypt(data1, dataKey, {iv: iv});
encrypted1 = encrypted1.toString();
// u0q0/g6w1lWwRyPKjbmr0BCGlN0po9y1djotJqY2IAB5yNVXih1bw7z6cyNP0d1duTRarsahEudeDkvTOzotL4egKsk8Il7Y/c0E6NuEK8Q=
The Base64 encoded IV used in both codes:
KCgWOBhGET1aIRMiFcIw
gives decoded:
28 28 16 38 18 46 11 3d 5a 21 13 22 15 c2 30
i.e. the IV has a length of 15 bytes and is therefore too short, which can easily be verified online, e.g. here. However, the aes-js code performs the decoding incorrectly and provides a 16 bytes IV:
28 28 16 38 18 46 11 3d 5a 21 13 22 15 c3 82 30
i.e. the decoding bug is the reason that the IV, which is actually too short, gets the right length. In contrast, the CryptoJS code performs the decoding correctly and then implicitly pads the too short IV with 0-bytes up to a length of 16 bytes:
28 28 16 38 18 46 11 3d 5a 21 13 22 15 c2 30 00
Thus, both codes use different IVs which produces different ciphertexts. The wrong decoding in the aes-js code happens in the line:
iv = new Uint8Array(aesjs.utils.utf8.toBytes(atob(iv)));
which is to be replaced by
var iv = base64ToByteArray(iv)[0];
analogous to the key. And of course an IV of the correct length, i.e. 16 bytes, must be used, otherwise the aes-js code will display a corresponding error message. Then, aes-js and CryptoJS code produce the same ciphertext.