crypto-js vs jsencrypt vs node-forge vs openpgp vs tweetnacl
JavaScript 暗号化ライブラリの選定:用途別アーキテクチャ比較
crypto-jsjsencryptnode-forgeopenpgptweetnacl類似パッケージ:

JavaScript 暗号化ライブラリの選定:用途別アーキテクチャ比較

crypto-jsjsencryptnode-forgeopenpgptweetnacl は、すべて JavaScript 環境で暗号化機能を提供するライブラリですが、その設計思想と適用範囲は大きく異なります。

crypto-js は、AES や SHA などの標準アルゴリズムをシンプルに利用するための「スイート」であり、学習コストが低く手軽に導入できます。jsencrypt は RSA 暗号に特化しており、フロントエンドでの公開鍵暗号化(例えばパスワードの送信時保護)を最小限の設定で実現します。

node-forge は TLS/SSL プロトコル実装や PKI(公開鍵基盤)操作など、低レベルな制御を必要とする高度なユースケースに対応する包括的なツールキットです。openpgp は PGP/GPG 標準に準拠した実装であり、メール署名やファイルの暗号化など、相互運用性が求められる場面で不可欠です。

最後に tweetnacl は、NaCl ライブラリの移植版であり、現代の暗号プリミティブ(Curve25519 など)に焦点を当て、最小限のコードサイズと最高レベルのセキュリティを重視する開発者向けに設計されています。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
crypto-js016,399487 kB2783年前MIT
jsencrypt06,804901 kB1461年前MIT
node-forge05,3271.65 MB4615ヶ月前(BSD-3-Clause OR GPL-2.0)
openpgp05,96617.4 MB363ヶ月前LGPL-3.0+
tweetnacl01,921-67年前Unlicense

JavaScript 暗号化ライブラリ深度比較:実装、用途、そして選定の指針

フロントエンド開発において、機密データを扱う際にどの暗号化ライブラリを選ぶかは、アプリケーションのセキュリティとパフォーマンスに直結する重要な決定です。crypto-jsjsencryptnode-forgeopenpgptweetnacl はそれぞれ異なる哲学に基づいて設計されており、万能な「ベスト」は存在しません。

本稿では、これらのライブラリが実際のコードでどのように振る舞うか、具体的な実装例を通じて比較し、アーキテクチャ決定に必要な洞察を提供します。

🔐 対称暗号化(AES):手軽さ vs 制御性

データを暗号化して保存・送信する際、最も一般的なのが AES です。ライブラリによって、その実装のしやすさと柔軟性が異なります。

crypto-js は、最も直感的な API を提供します。文字列を渡すだけで暗号化・復号が可能であり、モード(CBC など)や Padding の指定も簡単です。

// crypto-js: AES 暗号化
import CryptoJS from 'crypto-js';

const message = "Secret Data";
const key = "MySecretKey12345"; // 注意:実際は適切な長さの鍵を使用

// 暗号化
const encrypted = CryptoJS.AES.encrypt(message, key).toString();

// 復号
const bytes = CryptoJS.AES.decrypt(encrypted, key);
const decrypted = bytes.toString(CryptoJS.enc.Utf8);

tweetnacl は、AES をサポートしていません。代わりに、現代的な認証付き暗号である secretbox (XSalsa20 + Poly1305) を使用します。これにより、暗号化と同時に改ざん検知が可能になり、セキュリティが向上しますが、既存の AES システムとの互換性はありません。

// tweetnacl: 認証付き暗号 (secretbox)
import nacl from 'tweetnacl';
import naclUtil from 'tweetnacl-util';

const message = naclUtil.decodeUTF8("Secret Data");
const nonce = nacl.randomBytes(nacl.secretbox.nonceLength);
const key = nacl.randomBytes(nacl.secretbox.keyLength);

// 暗号化
const encryptedBox = nacl.secretbox(message, nonce, key);

// 復号
const decryptedMessage = nacl.secretbox.open(encryptedBox, nonce, key);
const result = naclUtil.encodeUTF8(decryptedMessage);

node-forge は、より低レベルな制御を提供します。バッファの操作や、暗号化モードの詳細な設定が可能です。AES を使用しますが、API はやや冗長になります。

// node-forge: AES-CBC 暗号化
import forge from 'node-forge';

const message = "Secret Data";
const key = forge.random.getBytesSync(16); // 128-bit key
const iv = forge.random.getBytesSync(16);

const cipher = forge.cipher.createCipher('AES-CBC', key);
cipher.start({ iv: iv });
cipher.update(forge.util.createBuffer(message, 'utf8'));
cipher.finish();

const encrypted = cipher.output.getBytes();

🔑 公開鍵暗号(RSA):特化型 vs 包括型

パスワードの送信や、特定の相手へのメッセージ送信には RSA が使われます。ここでの選定基準は「RSA だけできればいいか」か「証明書の操作なども必要か」です。

jsencrypt は、RSA に特化したライブラリです。公開鍵で暗号化し、秘密鍵で復号するという単純なフローに最適化されており、設定が極めて簡単です。

// jsencrypt: RSA 暗号化
import JSEncrypt from 'jsencrypt';

const encryptor = new JSEncrypt();
// 公開鍵を設定
encryptor.setPublicKey('-----BEGIN PUBLIC KEY-----...');

const message = "Sensitive Password";
const encrypted = encryptor.encrypt(message);

// 復号(秘密鍵が必要)
const decryptor = new JSEncrypt();
decryptor.setPrivateKey('-----BEGIN RSA PRIVATE KEY-----...');
const decrypted = decryptor.decrypt(encrypted);

node-forge は、RSA だけでなく鍵の生成や CSR 作成なども行えます。公開鍵の形式変換や、証明書チェーンの検証など、複雑な PKI 操作が必要な場合に真価を発揮します。

// node-forge: RSA 暗号化と鍵操作
import forge from 'node-forge';

// 公開鍵の PEM から変換
const publicKeyPem = '-----BEGIN PUBLIC KEY-----...';
const publicKey = forge.pki.publicKeyFromPem(publicKeyPem);

const message = "Sensitive Password";
// RSA-OAEP などで暗号化
const encrypted = publicKey.encrypt(message, 'RSA-OAEP', {
  md: forge.md.sha256.create()
});

// 出力はバイト列なので、必要に応じて Base64 等に変換
const encryptedBase64 = forge.util.encode64(encrypted);

📜 標準規格(PGP/GPG):相互運用性の確保

メールクライアントや既存の GPG ツールとデータをやり取りする場合、独自の実装ではなく標準準拠の実装が必要です。

openpgp は、OpenPGP 標準(RFC 4880)を実装した唯一の主要ライブラリです。鍵の生成から署名、暗号化まで、GPG と完全に互換性のある操作が可能です。

// openpgp: PGP 暗号化
import * as openpgp from 'openpgp';

const message = "Confidential Email Content";
const publicKeyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----...';

(async () => {
  const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });
  
  const encrypted = await openpgp.encrypt({
    message: await openpgp.createMessage({ text: message }),
    encryptionKeys: publicKey
  });
  
  console.log(encrypted); // PGP メッセージブロック
})();

他のライブラリ(crypto-jstweetnacl)は PGP フォーマットをサポートしていないため、この要件がある場合は openpgp 一択となります。

🛡️ ハッシュ化と署名:モダン vs レガシー

データの改ざん検知やパスワード保存にはハッシュ関数を使います。

crypto-js は、SHA-256 や SHA-1、MD5 などの幅広いアルゴリズムをサポートしています。レガシーシステムとの互換性を保つ必要がある場合に便利です。

// crypto-js: SHA-256 ハッシュ化
import CryptoJS from 'crypto-js';

const message = "Data to hash";
const hash = CryptoJS.SHA256(message).toString();

tweetnacl は、セキュリティの観点から安全とされる現代のアルゴリズムに注力しています。SHA-256 などは直接提供していませんが、Ed25519 による署名機能を提供しており、これはハッシュと鍵を組み合わせた高度な操作です。

// tweetnacl: Ed25519 署名
import nacl from 'tweetnacl';
import naclUtil from 'tweetnacl-util';

const message = naclUtil.decodeUTF8("Data to sign");
const keyPair = nacl.sign.keyPair(); // 鍵ペア生成

// 署名
const signature = nacl.sign(message, keyPair.secretKey);

// 検証
const isValid = nacl.sign.open(signature, keyPair.publicKey) !== null;

⚖️ 選定のまとめ:プロジェクト要件による明確な違い

各ライブラリは、特定の「痛み」を解決するために存在します。

特徴crypto-jsjsencryptnode-forgeopenpgptweetnacl
主な用途汎用暗号化 (AES, SHA)RSA 特化低レベル PKI/TLSPGP/GPG 互換現代暗号 (NaCl)
学習コスト低い非常に低い高い中〜高い
バンドルサイズ大きい (多機能)小さい非常に大きい大きい非常に小さい
アルゴリズム標準的・レガシーRSA のみ網羅的PGP 標準現代的・安全
相互運用性汎用汎用 RSA汎用GPG と互換NaCl エコシステム

具体的な選定ガイドライン

  1. 「とりあえず AES で暗号化したい」crypto-js が最適です。ドキュメントも豊富で、実装が最も早く終わります。ただし、アプリのサイズが大きくなっても許容できる場合に限ります。

  2. 「ログイン時のパスワードを公開鍵で暗号化したい」jsencrypt を選んでください。RSA 以外の機能は不要であり、これ以上の複雑さは不要です。

  3. 「ブラウザで証明書を生成したり、TLS 通信をシミュレートしたい」node-forge 以外に選択肢はありません。他のライブラリでは実現不可能な低レベル操作が可能です。

  4. 「GPG を使っているユーザーとメールを暗号化したい」openpgp が必須です。標準規格への準拠が絶対条件となるユースケースです。

  5. 「セキュリティを最優先し、コードサイズも最小にしたい」tweetnacl を採用すべきです。AES などの古い規格を使う必要がなく、現代的なアーキテクチャを構築できる場合に最大の効果を発揮します。

💡 結論:銀の弾丸はない

セキュリティライブラリの選定は、トレードオフの連続です。「手軽さ」を取ればサイズが増え、「サイズ」を取れば機能制限を受け、「互換性」を取れば実装が複雑になります。

プロジェクトがレガシーシステムとの接続を必要とするなら crypto-jsopenpgp が安定した選択となります。一方、新規開発でモダンなスタックを組むのであれば、tweetnacl のような軽量かつ安全なライブラリを検討する価値が大いにあります。また、特定のアルゴリズム(RSA など)のみが必要な場合は、jsencrypt のように特化したライブラリを使うことで、不要なコードをバンドルせずに済みます。

最終的には、プロジェクトが「何を暗号化したいか」だけでなく、「誰と通信したいか(互換性)」と「どのリソース環境で動かすか(サイズ)」の 3 点を満たすライブラリを選ぶことが、堅牢なフロントエンドアーキテクチャへの道となります。

選び方: crypto-js vs jsencrypt vs node-forge vs openpgp vs tweetnacl

  • crypto-js:

    crypto-js を選ぶべきは、AES、SHA、HMAC などの標準アルゴリズムをブラウザで手軽に使いたい場合です。設定が少なく API も直感的なため、プロトタイピングや複雑な鍵管理が不要なシンプルなプロジェクトに適しています。ただし、包括的なライブラリであるため、使わない機能までバンドルされやすく、最終的なバイナリサイズが大きくなるトレードオフがあります。

  • jsencrypt:

    jsencrypt は、RSA 暗号化のみを行いたい場合に最適です。特に、ログインフォームなどでパスワードを公開鍵で暗号化して送信するような、単一のユースケースに特化しています。PGP や複雑な証明書操作が不要で、とにかく手早く RSA を実装したい場合に選定されますが、それ以外の暗号機能には対応していません。

  • node-forge:

    node-forge は、TLS ソケットの作成、CSR(証明書署名依頼)の生成、PKCS#12 操作など、非常に低レベルな制御が必要な場合に選択します。ブラウザで完全な PKI 処理を行いたいなど、他では代替できない特殊な要件があるプロジェクト向けです。多機能である反面、API が複雑で学習曲線が急であり、単純な暗号化には過剰すぎる傾向があります。

  • openpgp:

    openpgp は、既存の PGP/GPG インフラストラクチャと互換性を持たせる必要がある場合に唯一の選択肢となります。メールの署名・暗号化や、PGP 鍵を用いたファイル保護など、標準準拠が必須のシナリオで使用します。機能が強力な分、鍵の管理や設定が複雑になるため、PGP 標準が不要な場合は他の軽量ライブラリを検討すべきです。

  • tweetnacl:

    tweetnacl は、バンドルサイズを極限まで小さく抑えつつ、現代的な暗号標準(Curve25519, ChaCha20 など)を使用したい場合に選定されます。セキュリティ監査を受けやすく、設計がシンプルであるため、信頼性を最優先するプロジェクトに向いています。ただし、AES や SHA-1 などのレガシーアルゴリズムはサポートしていないため、既存システムとの互換性が必要な場合には不向きです。

crypto-js のREADME

crypto-js

JavaScript library of crypto standards.

Discontinued

Active development of CryptoJS has been discontinued. This library is no longer maintained.

Nowadays, NodeJS and modern browsers have a native Crypto module. The latest version of CryptoJS already uses the native Crypto module for random number generation, since Math.random() is not crypto-safe. Further development of CryptoJS would result in it only being a wrapper of native Crypto. Therefore, development and maintenance has been discontinued, it is time to go for the native crypto module.

Node.js (Install)

Requirements:

  • Node.js
  • npm (Node.js package manager)
npm install crypto-js

Usage

ES6 import for typical API call signing use case:

import sha256 from 'crypto-js/sha256';
import hmacSHA512 from 'crypto-js/hmac-sha512';
import Base64 from 'crypto-js/enc-base64';

const message, nonce, path, privateKey; // ...
const hashDigest = sha256(nonce + message);
const hmacDigest = Base64.stringify(hmacSHA512(path + hashDigest, privateKey));

Modular include:

var AES = require("crypto-js/aes");
var SHA256 = require("crypto-js/sha256");
...
console.log(SHA256("Message"));

Including all libraries, for access to extra methods:

var CryptoJS = require("crypto-js");
console.log(CryptoJS.HmacSHA1("Message", "Key"));

Client (browser)

Requirements:

  • Node.js
  • Bower (package manager for frontend)
bower install crypto-js

Usage

Modular include:

require.config({
    packages: [
        {
            name: 'crypto-js',
            location: 'path-to/bower_components/crypto-js',
            main: 'index'
        }
    ]
});

require(["crypto-js/aes", "crypto-js/sha256"], function (AES, SHA256) {
    console.log(SHA256("Message"));
});

Including all libraries, for access to extra methods:

// Above-mentioned will work or use this simple form
require.config({
    paths: {
        'crypto-js': 'path-to/bower_components/crypto-js/crypto-js'
    }
});

require(["crypto-js"], function (CryptoJS) {
    console.log(CryptoJS.HmacSHA1("Message", "Key"));
});

Usage without RequireJS

<script type="text/javascript" src="path-to/bower_components/crypto-js/crypto-js.js"></script>
<script type="text/javascript">
    var encrypted = CryptoJS.AES(...);
    var encrypted = CryptoJS.SHA256(...);
</script>

API

See: https://cryptojs.gitbook.io/docs/

AES Encryption

Plain text encryption

var CryptoJS = require("crypto-js");

// Encrypt
var ciphertext = CryptoJS.AES.encrypt('my message', 'secret key 123').toString();

// Decrypt
var bytes  = CryptoJS.AES.decrypt(ciphertext, 'secret key 123');
var originalText = bytes.toString(CryptoJS.enc.Utf8);

console.log(originalText); // 'my message'

Object encryption

var CryptoJS = require("crypto-js");

var data = [{id: 1}, {id: 2}]

// Encrypt
var ciphertext = CryptoJS.AES.encrypt(JSON.stringify(data), 'secret key 123').toString();

// Decrypt
var bytes  = CryptoJS.AES.decrypt(ciphertext, 'secret key 123');
var decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));

console.log(decryptedData); // [{id: 1}, {id: 2}]

List of modules

  • crypto-js/core
  • crypto-js/x64-core
  • crypto-js/lib-typedarrays

  • crypto-js/md5
  • crypto-js/sha1
  • crypto-js/sha256
  • crypto-js/sha224
  • crypto-js/sha512
  • crypto-js/sha384
  • crypto-js/sha3
  • crypto-js/ripemd160

  • crypto-js/hmac-md5
  • crypto-js/hmac-sha1
  • crypto-js/hmac-sha256
  • crypto-js/hmac-sha224
  • crypto-js/hmac-sha512
  • crypto-js/hmac-sha384
  • crypto-js/hmac-sha3
  • crypto-js/hmac-ripemd160

  • crypto-js/pbkdf2

  • crypto-js/aes
  • crypto-js/tripledes
  • crypto-js/rc4
  • crypto-js/rabbit
  • crypto-js/rabbit-legacy
  • crypto-js/evpkdf

  • crypto-js/format-openssl
  • crypto-js/format-hex

  • crypto-js/enc-latin1
  • crypto-js/enc-utf8
  • crypto-js/enc-hex
  • crypto-js/enc-utf16
  • crypto-js/enc-base64

  • crypto-js/mode-cfb
  • crypto-js/mode-ctr
  • crypto-js/mode-ctr-gladman
  • crypto-js/mode-ofb
  • crypto-js/mode-ecb

  • crypto-js/pad-pkcs7
  • crypto-js/pad-ansix923
  • crypto-js/pad-iso10126
  • crypto-js/pad-iso97971
  • crypto-js/pad-zeropadding
  • crypto-js/pad-nopadding

Release notes

4.2.0

Change default hash algorithm and iteration's for PBKDF2 to prevent weak security by using the default configuration.

Custom KDF Hasher

Blowfish support

4.1.1

Fix module order in bundled release.

Include the browser field in the released package.json.

4.1.0

Added url safe variant of base64 encoding. 357

Avoid webpack to add crypto-browser package. 364

4.0.0

This is an update including breaking changes for some environments.

In this version Math.random() has been replaced by the random methods of the native crypto module.

For this reason CryptoJS might not run in some JavaScript environments without native crypto module. Such as IE 10 or before or React Native.

3.3.0

Rollback, 3.3.0 is the same as 3.1.9-1.

The move of using native secure crypto module will be shifted to a new 4.x.x version. As it is a breaking change the impact is too big for a minor release.

3.2.1

The usage of the native crypto module has been fixed. The import and access of the native crypto module has been improved.

3.2.0

In this version Math.random() has been replaced by the random methods of the native crypto module.

For this reason CryptoJS might does not run in some JavaScript environments without native crypto module. Such as IE 10 or before.

If it's absolute required to run CryptoJS in such an environment, stay with 3.1.x version. Encrypting and decrypting stays compatible. But keep in mind 3.1.x versions still use Math.random() which is cryptographically not secure, as it's not random enough.

This version came along with CRITICAL BUG.

DO NOT USE THIS VERSION! Please, go for a newer version!

3.1.x

The 3.1.x are based on the original CryptoJS, wrapped in CommonJS modules.