The Advanced Encryption Standard, or AES, is the standard chosen by the U.S. government to protect messages with symmetric encryption.
Understanding what AES does and how it works is important. So is understanding how to use is practically in a real program.
We’ll first take a look at the Node.js implementation of AES. For now, we’ll focus on the built-in Node.js Crypto module.
The first step is to create a key to use to encrypt your sensitive data. This can be created using a Password-Based Key Derivation Function, or PBKDF.
I’ll talk about PBKDFs in another post. In short, PBKDFs can be used to turn a user’s password into a good random key for use in encrypting sensitive information.
Let’s assume that you have a key created. This key can be saved in an object. Let’s call this object config:
This object also contains the name of the algorithm we want to use, which in this case is AES256. This represents the AES algorithm using a 256-bit key.
Encrypting data in Node.js using the Crypto module has three steps:
- Create the initialization vector required by AES’s Cipher Block Chaining (CBC).
- Create a cipher object using the initialization vector and key.
- Encrypt the data using the cipher object created in step 2.
The Initialization Vector
There is a subtlety to creating a cipher in Node that could lead developers to create an insecure cipher implementation.
According the the docs, Node uses OpenSSL to create ciphers. Apparently, the default functionality used by the createCipher function in Crypto derives keys with an MD5 hash, no salt, and one iteration. This means that an attacker could brute-force the password (key) used to create the IV and cipher. Once guessed, the encryption is useless.
So that is why we need to create our own Initialization Vector using a PBKDF function. This will allow us to pass in a good random IV value that will ensure randomness throughout the encryption process. Check out my post on encryption basics to see how an IV is used within an algorithm such as AES.
Now for some code:
This function returns a random IV created by the Crypto module’s pbkdf2Sync function. This function takes a key, salt, the number of iterations, the output length, and hash function to use.
To create the salt, we simply use the randomBytes function of the Crypto module. By passing in the number of bytes and no callback, this function will be executed synchronously. 16 bytes, or 128-bits, is the NIST standard for salt length, so it’s a good place to start.
Creating the Cipher Object
Now that the IV has been created, we create the cipher object using the createCipheriv function. This function allows us to pass in the IV we created along with the algorithm to use and the key.
We now have a cipher object ready to encrypt the data that we need to protect.
The decrypt function creates a Decipher object in a similar way, by using the createDecipheriv function. This object will decrypt the data when we need to use it.
Encrypting and Decrypting the Data
Now it is time to use the cipher we have created to encrypt the data. This part may seem kind of weird but this is how the API was written.
First, we call Cipher.update, passing in the data to encrypt along with input and output encodings. You can call update with new data as many times as you want.
When you are done adding data using the update function, you call the final function. The final function will return the enciphered bits to you and effectively “seal” the Cipher object.
If you call final more than once, an error is thrown. If you call update after final is called, an error is thrown. You must call final at the end to finish the encryption.
The Decipher object works in a similar fashion. Add the encrypted data using the Decipher.update function, then call final when done. The same rules apply here as with the Cipher object.
Let’s Go Encrypt with Node
Those are the basics of using AES256 with Node.js and its built-in Crypto module. The code featured here can be seen on my fork of the OWASP NodeGoat project in the tutorial page.