Welcome to new things

[Technical] [Electronic work] [Gadget] [Game] memo writing

AES encryption and decryption in Node.js

I had a chance to do AES encryption/decryption in Node.js, so here are my notes.

The encryption algorithm "AES-256-CBC" was used.

About AES CBC encryption in a nutshell

AES CBC is an encryption algorithm that encrypts binary data of arbitrary length using a key and decrypts it using the same key.

However, if encryption is done with just a key, if the original data is the same, the encrypted data will also be the same each time, making it easier to decrypt.

Therefore, in AES CBC, encryption is performed using not only the key but also "the key + an arbitrary value set for each encryption" and decryption is performed using the "key + an arbitrary value set for each encryption" so that the same data can be decrypted with different encryption results each time, making it difficult to decipher.

The "arbitrary value to be set for each encryption" is then called the IV (initialization vector).

About the Key

The key must be shared in advance between the encryptor and decryptor, and the key must not be divulged to outside parties.

The content of the key can be any value of 128 bits (16 bytes) for AES128 or 256 bits (32 bytes) for AES256.

About IV

Unlike keys, IVs have no problem leaking to the outside world.

The size of IV is equal to the block size of the encryption, and since the block size of AES is 128 bits, IV can be any value of 128 bits (16 bytes).

The specifications of how the IV value is generated and passed from the encrypting side to the decrypting side are not fixed and are left to the implementation.

It is often the case that IV is generated with a random value at the time of encryption, and IV is given at the beginning of the encrypted data and passed to the decryptor. (This is what we did in this case).

Passing Encrypted Data

The encrypted data is binary data, and there is no set specification for how it is conveyed to the decryptor, leaving this to the implementation.

Since character strings are easier to handle than binary data, encrypted data is converted into character strings using Base64 and sent to the decryption side, Therefore, it is often the case that the encrypted data is converted into a string using Base64 and sent to the decryption side, and the decryption side converts the string back into binary data and decrypts it. (This is what we did in this case).

Node.js Implementation Example

Based on the above, this is an example implementation in Node.js.

import * as crypto from 'crypto'

const ALGORITHM = 'aes-256-cbc';
const KEY = Buffer.from([
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
]); // Use a 256-bit value with all bits 1 as the key.

function encodeBase64(data: string): string {
    // Generate a 16-byte random value and set it as IV
    const iv = crypto.randomBytes(16);

    // Cryptograph Creation
    const cipher = crypto.createCipheriv(ALGORITHM, KEY, iv);

    // Binary encryption of data
    const encData = cipher.update(Buffer.from(data));

    // End processing & add iv at the beginning and return binary as base64 (string)
    return Buffer.concat([iv, encData, cipher.final()]).toString('base64');
}

function decodeBase64(data: string): string {
    // Convert received encrypted string to binary
    const buff = Buffer.from(data, 'base64');

    // Extract the first 16 bytes, which is the iv value.
    const iv = buff.slice(0, 16);

    // Extract the encrypted data after the iv value.
    const encData = buff.slice(16);

    // Restores are made
    const decipher = crypto.createDecipheriv(ALGORITHM, KEY, iv);

    // Decrypt encrypted data
    const decData = decipher.update(encData);

    // Terminate & convert binary back to string
    return Buffer.concat([decData, decipher.final()]).toString('utf8');
}

(() => {
    const data = "Hello World!";
    console.log(`INPUT : ${data}`);
    // INPUT : Hello World!

    const encData = encodeBase64(data);
    console.log(`ENCODE : ${encData}`);
    // ENCODE : 0XTIPX06EAClUAFNdT+6EDlv+bOrB6plqkGzd0hEvdU=

    const decData = decodeBase64(encData);
    console.log(`DECODE : ${decData}`);
    // DECODE : Hello World!
})();

Brief description

  • encryption

    • With cipher.update(<buffer>), the data is encrypted and the encrypted data is returned in binary
    • Repeat cipher.update(<buffer>) if data is long
    • After all encryption is complete, call cipher.final() to get the end of the encrypted data
    • The concatenation of a series of data becomes the final encrypted data
  • decoding

    • With decipher.update(<buffer>), the encrypted data is decrypted and returned in binary
    • Get the end of the decoded data with decipher.final().
    • The concatenation of those data becomes the final decrypted data

About Key Generation

When we say "key," we generally think of something like a key phrase, A key in AES is a 256-bit value, not a string of characters.

However, a string like a key phrase is easier for humans to handle, An example of using a hash function to generate a 256-bit (32-byte) value from a string as a key is given in the Node.js Crypto documentation.

Example

const KEY = crypto.scryptSync("キーフレーズ", "", 32);

Another example would be to use a string of 32 ASCII characters as the key. However, this would limit the value to those assigned to ASCII characters, so the value would be more limited than 256 bits.

Example

const KEY = Buffer.from("01234567890123456789012345678912", 'ascii');

impressions

At first, I was hooked because I thought the key was a string.

Binaries and strings come and go, so you have to follow them carefully.

As far as the encryption/decryption part is concerned, "data before encryption," "data after encryption," "key," and "IV" are all binary, and they are only mathematically operated on, so I think it would be better not to mix other types, but to handle them on a binary basis and return to the original type at the end to avoid confusion.

Reference Articles

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com