Unzipping encrypted compressed report from Amazon Selling Partner


Question: Unzipping encrypted compressed report from Amazon Selling Partner

I am using the getReport operation to fetch the documentId, which later I use to download the report document itself which is encrypted and compressed.

The code looks like this:

const documentData = await this.sellingPartner.callAPI({   operation: "getReportDocument",   endpoint: "reports",   path: { reportDocumentId: reportData.reportDocumentId } })  const request = https.get(documentData.url, function(res) {   const data = [];   res.on("data", chunk => data.push(chunk));   res.on("end", () => {     const key = new Buffer.from(documentData.encryptionDetails.key, 'base64')     const initializationVector = new Buffer.from(documentData.encryptionDetails.initializationVector, 'base64')      const input = Buffer.concat(data)      let result;     try {       result = aes.decryptText(         aes.CIPHERS.AES_256,         key,         initializationVector,         input       )     } catch (e) {       console.log(e)     }      console.log(">>>>")     console.log(result)      zlib.gunzip(result, (err, unzipped) => {       debugger     });   }); } 

The current error I am getting is from zlib:

Error: incorrect header check     at Zlib.zlibOnError [as onerror] (node:zlib:189:17) 

I am getting the same even if I pass the unencrypted value directly to zlib.

There is a Sample Java code example in the docs, but I cannot understand very well where they do the decryption: before unzipping or after?

In any case, what is the right way to solve this: unzip and decrypt or decrypt and unzip? The former javascript error does not work at all, the latter almost works but fails at the unzipping part.

How can I solve the unzip problem?

Total Answers: 1


Answers 1: of Unzipping encrypted compressed report from Amazon Selling Partner

Short Answer: Decrypt first and then decompress if compression type is specified.

Longer answer:

After a little research I stumbled upon this example (sadly written javascript error in python not in javascript) but it covers the steps to do in more detail.


It contains both compressed or not compressed cases. Specifically for compressed it looks like this:

import gzip import requests  from cryptography.hazmat.primitives import padding from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes  def ase_cbc_decryptor(key, iv, encryption):     cipher = Cipher(algorithms.AES(base64.b64decode(key)), modes.CBC(base64.b64decode(iv)))     decryptor = cipher.decryptor()     decrypted_text = decryptor.update(encryption)     unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()     unpaded_text = unpadder.update(decrypted_text)     return unpaded_text + unpadder.finalize()  def get_report_document_content(self, key, iv, url, compression_type=None):     resp = requests.get(url=url)     resp_content = resp.content      decrypted_content = ase_cbc_decryptor(key=key, iv=iv, encryption=resp_content)      if compression_type == 'GZIP':        decrypted_content = gzip.decompress(decrypted_content)      code = 'utf-8'     if 'cp1252' in resp.headers.get('Content-Type', '').lower():         code = 'Cp1252'     return decrypted_content.decode(code) 

P.S. keep in mind you need to use AES in CBC mode

Short example here: https://gist.github.com/manuks/5cef1e536ef791e97b39

var keyhex = "8479768f48481eeb9c8304ce0a58481eeb9c8304ce0a5e3cb5e3cb58479768f4"; //length 32   var blockSize = 16;  function encryptAES(input) {     try {         var iv = require('crypto').randomBytes(16);         //console.info('iv',iv);         var data = new Buffer(input).toString('binary');         //console.info('data',data);                  key = new Buffer(keyhex, "hex");         //console.info(key);         var cipher = require('crypto').createCipheriv('aes-256-cbc', key, iv);         // UPDATE: crypto changed in v0.10          // https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10           var nodev = process.version.match(/^v(\d+)\.(\d+)/);          var encrypted;          if( nodev[1] === '0' && parseInt(nodev[2]) < 10) {             encrypted = cipher.update(data, 'binary') + cipher.final('binary');         } else {             encrypted =  cipher.update(data, 'utf8', 'binary') +  cipher.final('binary');         }          var encoded = new Buffer(iv, 'binary').toString('hex') + new Buffer(encrypted, 'binary').toString('hex');          return encoded;     } catch (ex) {       // handle error       // most likely, entropy sources are drained       console.error(ex);     } } function decryptAES(encoded) {       var combined = new Buffer(encoded, 'hex');            key = new Buffer(keyhex, "hex");          // Create iv     var iv = new Buffer(16);          combined.copy(iv, 0, 0, 16);     edata = combined.slice(16).toString('binary');      // Decipher encrypted data     var decipher = require('crypto').createDecipheriv('aes-256-cbc', key, iv);      // UPDATE: crypto changed in v0.10     // https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10       var nodev = process.version.match(/^v(\d+)\.(\d+)/);      var decrypted, plaintext;     if( nodev[1] === '0' && parseInt(nodev[2]) < 10) {           decrypted = decipher.update(edata, 'binary') + decipher.final('binary');             plaintext = new Buffer(decrypted, 'binary').toString('utf8');     } else {         plaintext = (decipher.update(edata, 'binary', 'utf8') + decipher.final('utf8'));     }     return plaintext; } var input="testing";  var encrypted = encryptAES(input); console.info('encrypted:', encrypted); var decryped = decryptAES(encrypted); console.info('decryped:',decryped);