Encrypting Data With Node.js

In a recent side project I came upon the need to securely store credentials in a database that I can later retrieve and use as part of the application. I know that Node.js has a very extensive crypto module, so I knew it was possible, though I wasn’t sure on the specifics.

Typically when storing passwords or other similar secrets, values are hashed but in my case I needed to encrypt/decrypt data since I need to access the original value after accessing it from my database. To make this work, we can use the createCipheriv function along with a secret key and initialization vector (IV) to encrypt and decrypt our data.

import crypto from "node:crypto"

const algorithm = "aes-256-cbc"
const secretKey = process.env.SECRET_KEY

if (!secretKey) {
  throw new Error("SECRET_KEY environment variable is required")
}

const key = crypto
  .createHash("sha512")
  .update(secretKey)
  .digest("hex")
  .substring(0, 32)

const iv = crypto.randomBytes(16)

export function encrypt(data: string) {
  const cipher = crypto.createCipheriv(algorithm, Buffer.from(key), iv)
  let encrypted = cipher.update(data, "utf-8", "hex")
  encrypted += cipher.final("hex")

  // Package the IV and encrypted data together so it can be stored in a single
  // column in the database.
  return iv.toString("hex") + encrypted
}

export function decrypt(data: string) {
  // Unpackage the combined iv + encrypted message. Since we are using a fixed
  // size IV, we can hard code the slice length.
  const inputIV = data.slice(0, 32)
  const encrypted = data.slice(32)
  const decipher = crypto.createDecipheriv(
    algorithm,
    Buffer.from(key),
    Buffer.from(inputIV, "hex"),
  )

  let decrypted = decipher.update(encrypted, "hex", "utf-8")
  decrypted += decipher.final("utf-8")
  return decrypted
}

The nice thing about this implementation is that it returns a plain string with the IV and encrypted data sandwiched together. As long as you use the decrypt function, it knows how to properly decrypt the value making it really simple to store in a database.

const password = "my secret password, don't tell anyone!"
const encrypted = encrypt(password)
const decrypted = decrypt(encrypted)

I’ll probably also explore re-implementing this same logic using the web crypto API. But that’s for another day!