Search code examples
node.jszlibcryptojs

Encrypt compressed data using zlib and cryptojs


I'm in node js, trying to compress an object using zlib.deflate and then encrypt it using cryptojs AES.

Then, when I try to get the original value, I fail on the zlib.inflate call with the error: "incorrect header check".

const zlib = require('zlib');
const { AES, enc } = require('crypto-js');

const obj = {
    a: "a",
    b: "b"
};

const inputToDeflate = JSON.stringify(obj);
const compressed = zlib.deflateSync(inputToDeflate);
const encrypted = AES.encrypt(compressed.toString(), 'f11').toString();


const decrypted = AES.decrypt(encrypted, 'f11');
const decompressed = zlib.inflateSync(decrypted.toString(enc.Utf8));

Solution

  • I think you're losing zlib header when converting it to string before encryption. We need to change this compressed.toString() some way that the header still preserve inside, something like base64 encoding.

    const buffer = Buffer.from(compressed).toString("base64")
    const encrypted = AES.encrypt(buffer, 'f11').toString();
    

    Decryption is remind the same, but we need to decode the result back from base64 to original zlib data like before.

    const decrypted = AES.decrypt(encrypted, 'f11').toString(enc.Utf8);
    const decryptedBuffer = Buffer.from(decrypted,'base64')
    const decompressed = zlib.inflateSync(decryptedBuffer);
    
    const resultString = decompressed.toString()
    

    That's it. But I have suggestion here to do encryption part first before compression. Because the result string is different. Take a look in this full code :

    Full Code

    const zlib = require('zlib');
    const { AES, enc } = require('crypto-js');
    
    const obj = {
        a: "a",
        b: "b"
    };
    
    const inputToDeflate = JSON.stringify(obj);
    const compressed = zlib.deflateSync(inputToDeflate);
    
    // Compact the data to base64
    const buffer = Buffer.from(compressed).toString("base64")
    const encrypted = AES.encrypt(buffer, 'f11').toString();
    
    console.log("compressed + base64 + encrypted length",encrypted.length)
    
    const decrypted = AES.decrypt(encrypted, 'f11').toString(enc.Utf8);
    
    // Convert the result from base64 to original compressed data
    const decryptedBuffer = Buffer.from(decrypted,'base64')
    const decompressed = zlib.inflateSync(decryptedBuffer);
    
    // Convert buffer to string
    const resultString = decompressed.toString()
    
    // vice versa
    console.log(resultString)
    

    Output:

    compressed + base64 + encrypted length 88
    {"a":"a","b":"b"}
    

    Suggestion

    const zlib = require('zlib');
    const { AES, enc } = require('crypto-js');
    
    const obj = {
        a: "a",
        b: "b"
    };
    
    const inputData = JSON.stringify(obj);
    const encrypted = AES.encrypt(inputData, 'f11').toString();
    const compressed = zlib.deflateSync(encrypted);
    
    console.log("encrypted + compressed length",encrypted.length)
    
    const decompressed = zlib.inflateSync(compressed);
    const decrypted = AES.decrypt(decompressed.toString(), 'f11').toString(enc.Utf8);
    
    console.log(decrypted)
    

    Output:

    encrypted + compressed length 64
    {"a":"a","b":"b"}
    

    I think it much more efficient to do encryption first and then compression.