node.jsencryptionblock-cipher

nodejs recover createCipher data with createCipheriv


I have some encrypted data in my database

I did it few years ago using crypto.createCipher

const cipher = crypto.createCipher('aes192', password);

As createCipher and createDecipher is deprecated, I would like to change to createCipheriv and createDecipheriv. The problem is that the data I have in my database are encoded without iv. Is it possible to decode with createDecipheriv data encoded with createDecipher and to generate the same secret with createCipher and createCipheriv.

I tried setting the iv to null but not working

Thanks, because the database migration is an heavy work !


Solution

  • I tried setting the iv to null but not working

    This is because this method didn’t allow for passing an initialization vector (IV), and instead derived the IV from the key using the OpenSSL EVP_BytesToKey derivation function, using a null salt meaning that the IV would be deterministic for a given key which is an issue for ciphers with counter mode like CTR, GCM and CCM.

    Looking at your code:

     const cipher = crypto.createCipher('aes192', password);
    

    If you want to make this code backwards compatible, you need to call OpenSSL’s EVP_BytesToKey function yourself, typically through evp_bytestokey module which makes it available in JS userland.

    Is it possible to decode with createDecipheriv data encoded with createDecipher and to generate the same secret with createCipher and createCipheriv.

    Yes, you can. check out my example code here:

    const crypto = require('crypto');
    const EVP_BytesToKey = require('evp_bytestokey')
    const ALGO = 'aes192';
    const password = 'Your_Password_Here';
    const KEY_SIZE = 24;
    
    function decrypt_legacy_using_IV(text) {
        const result = EVP_BytesToKey(
          password,
          null,
          KEY_SIZE * 8, // byte to bit size
          16
        )
    
        let decipher = crypto.createDecipheriv(ALGO, result.key, result.iv);
        let decrypted = decipher.update(text, 'hex','utf8') + decipher.final('utf8');
        return decrypted.toString();
    }
    
    function encrypt_legacy_using_IV(text) {
        const result = EVP_BytesToKey(
          password,
          null,
          KEY_SIZE * 8, // byte to bit size
          16
        )
    
        var cipher = crypto.createCipheriv(ALGO, result.key, result.iv);
        var encrypted = cipher.update(text, 'utf8', 'hex') + cipher.final('hex');
        return encrypted.toString();
    }
    
    

    For complete running example, clone node-snippets and run node apogee-legacy-crypto-cipheriv.js.

    However the reason this function is deprecated in the first place is because you shouldn’t use it, and instead use a random unpredictable IV, which requires you to change your code to something like this:

    const iv = crypto.randomBytes(16)
    const cipher = crypto.createCipheriv('aes192', password, iv)
    

    Here, for AES-192 in CBC mode (aes192 being aliased to AES-192-CBC by OpenSSL), the IV size is expected to be the same as the block size, which is always 16 bytes.

    In order to decrypt the message, you will need the IV as well. Typically you’d store the IV together with the message, as the important part is for the IV to not be predictable ahead of time.