🔐

Node.js Crypto モジュールと CryptoJS による暗号化・復号化

2025/03/13に公開

モジュールやライブラリの整理

Crypto モジュール

まず Crytpo モジュールというのは Node.js に付属するもので、最新のブラウザでもこの crypto モジュールは導入されているらしい。

The node:crypto module provides cryptographic functionality that includes a set of wrappers for OpenSSL's hash, HMAC, cipher, decipher, sign, and verify functions.
https://nodejs.org/api/crypto.html#crypto

ただし、React や Angular などのフレームワークで開発する時は..

https://caniuse.com/?search=crypto

Crypto モジュールにしかないもの

  • scryptSync()
  • pbkdf2Sync()

上記は後述の CryptJS ライブラリにも crypto-browserify にも搭載されていない。pbkdf2Sync() についてはこれで暗号化しても pbkdf2() を使って復号するのは可能だった。

CryptJS

名前通りJavaScriptのスタンダードライブラリ。

https://www.npmjs.com/package/crypto-js

Nowadays, NodeJS and modern browsers have a native Crypto module.

https://cryptojs.gitbook.io/docs

crypt-browserify

上記の CryptJS ライブラリの説明で書かれている、モダンブラウザに標準搭載されているというのがこの Crypto Browserify?

https://www.npmjs.com/package/crypto-browserify

Here is the subset that is currently implemented:
createHash (sha1, sha224, sha256, sha384, sha512, md5, rmd160)
createHmac (sha1, sha224, sha256, sha384, sha512, md5, rmd160)
pbkdf2
pbkdf2Sync
randomBytes
pseudoRandomBytes
createCipher (aes)
createDecipher (aes)
createDiffieHellman
createSign (rsa, ecdsa)
createVerify (rsa, ecdsa)
createECDH (secp256k1)
publicEncrypt/privateDecrypt (rsa)
privateEncrypt/publicDecrypt (rsa)

暗号化と復号化のやり方

ここでは AES-256-CBC という形式での暗号化・復号化を記載。以下の4つの値が分かっていれば暗号化されたメッセージを復号できる。

const passward = 
const salt = 
const iv =
const encryptedMessage = 

事前準備

まず Crypt モジュールは標準搭載されているので CryptJS だけインストールする必要がある。

npm i crypto-js @types/crypto-js

Encrypt with Crypto

const message = 'secret message'
const password = 'password'
const salt = crypto.randomBytes(16);
const iv = crypto.randomBytes(16)

const key: Buffer = crypto.pbkdf2Sync(password, salt, 1000, 32, 'sha256')
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv)
encryptedMessage = Buffer.concat([
    cipher.update(message),
    cipher.final()
])

Encrypt with CryptoJS

ChatGPT出力コードで未検証。

const message = 'secret message';
const password = 'password';
const salt = CryptoJS.lib.WordArray.random(16);
const iv = CryptoJS.lib.WordArray.random(16);

const key = CryptoJS.PBKDF2(password, salt, {
    keySize: 256 / 32,
    iterations: 1000,
    hasher: CryptoJS.algo.SHA256,
});

const encrypted = CryptoJS.AES.encrypt(message, key, {
    iv: iv,
    padding: CryptoJS.pad.Pkcs7,
    mode: CryptoJS.mode.CBC
});

const encryptedMessage = encrypted.toString();

Decrypt with Crypto

const key: Buffer = crypto.pbkdf2Sync(password, salt, 1000, 32, 'sha256')
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv)
decryptedMessage =  Buffer.concat([
    decipher.update(encryptedMessage),
    decipher.final()
])

Decrypt with CrpytoJS

ChipherParams を使う方法

const key: WordArray = CryptoJS.PBKDF2(password, salt, {
    keySize: 256 / 32,
    iterations: 1000,
    hasher: CryptoJS.algo.SHA256,
});

const cipherParams = CryptoJS.lib.CipherParams.create({
    ciphertext: encryptedMessage,
    iv: iv,
    padding: CryptoJS.pad.Pkcs7,
});

const decryptedMessage = CryptoJS.AES.decrypt(cipherParams, key, {
    iv,
});
const decryptedMessageString = decrypted.toString(CryptoJS.enc.Hex);

直接 Decrypt する方法

ChatGPT出力コードで未検証。

const decrypted = CryptoJS.AES.decrypt(encryptedMessage, key, {
    iv: iv,
    padding: CryptoJS.pad.Pkcs7,
    mode: CryptoJS.mode.CBC
});

const decryptedMessageString = decrypted.toString(CryptoJS.enc.Utf8);

Hex, WordArray の String 変換

暗号化・復号化、両モジュール・ライブラリの相互利用する際に Hex, WordArray を String に変換する機会が多かったのでメモ。

Hex -> String

CryptoJS.enc.Hex.parse(x)

WordArray -> String

x.toString(CryptoJS.enc.Hex)?

参考文献

https://zenn.dev/maztak/scraps/402f1cb0ea6c91
https://qiita.com/qrusadorz/items/3521f90a727fef61ae63
https://qiita.com/hm0429/items/2acee723170b32b91304

Discussion