Search code examples
node.jsencryptioncryptography

How to encrypt decrypt with Node.js crypto module?


I'm trying to encrypt and decrypt text using the 'aes-256-cbc' algorithm and using URL safe encoding. Here's what code I've managed to write so far.

import crypto from 'node:crypto';

function encrypt(text) {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv(
    'aes-256-cbc',
    Buffer.from(process.env.SECRET_KEY!),
    iv
  );
  let encrypted = cipher.update(text, 'utf8', 'base64');
  encrypted += cipher.final('base64');

  // not sure what to do with the IV

  // make encoded text URL safe
  const encryptedText = result.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
  return encryptedText
}

function decrypt(encryptedText) {
  encryptedText = encryptedText.replace(/-/g, '+').replace(/_/g, '/');
  
  let decipher = crypto.createDecipheriv(
    'aes-256-cbc',
    Buffer.from(process.env.SECRET_KEY!),
    iv
  );
}

My questions are:

  1. How do you combine the IV with the encrypted data? Or do you not need to do this? Some tutorials I've seen combine the IV with the encrypted data while others do not
  2. Am I using base64 correctly? I'm not sure if I'm passing base64 as a parameter at the right time or not

Solution

  • How do you combine the IV with the encrypted data? Or do you not need to do this? Some tutorials I've seen combine the IV with the encrypted data while others do not

    When decrypting, the IV of the encryption must be used, which is why the IV must be passed to the decrypting side. Since the IV is not secret, it is usually concatenated with the ciphertext.
    Note that a static IV that is known to both sides is not a solution, as this results in reuse of key-IV pairs for fixed keys, which is a vulnerability. To avoid this, a random IV is often used (as it is already done in the posted code).

    Am I using base64 correctly? I'm not sure if I'm passing base64 as a parameter at the right time or not

    When encrypting, it is most convenient to concatenate IV, the result of the update()-call and the final()-call as Buffer and to convert this into a Base64url string using the specifier base64url. The decryption must be implemented accordingly.

    Sample:

    import crypto from 'node:crypto';
    
    var key = Buffer.from('MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=', 'base64');
    
    function encrypt(plaintext) {
      const iv = crypto.randomBytes(16);
      const cipher = crypto.createCipheriv(
        'aes-256-cbc',
        key,
        iv
      );
      let encrypted = Buffer.concat([iv, cipher.update(plaintext, 'utf8'), cipher.final()]);
      return encrypted.toString('base64url');
    }
    
    function decrypt(ivCiphertextB64) {
      const ivCiphertext = Buffer.from(ivCiphertextB64, 'base64url');
      const iv = ivCiphertext.subarray(0, 16);
      const ciphertext = ivCiphertext.subarray(16);
      const cipher = crypto.createDecipheriv(
        'aes-256-cbc',
        key,
        iv
      );
      let decrypted = Buffer.concat([cipher.update(ciphertext), cipher.final()]);
      return decrypted.toString('utf-8');
    }
    
    var ct = encrypt('The quick brown fox jumps over the lazy dog');
    var dt = decrypt(ct);
    console.log(ct);
    console.log(dt);