crypto-js、jsencrypt、node-forge、openpgp 和 tweetnacl 都是 JavaScript 生态中用于处理密码学操作的库,但它们的侧重点和适用场景截然不同。crypto-js 是一个全面的哈希和对称加密工具集,适合处理密码哈希和简单的数据混淆。jsencrypt 专注于 RSA 算法的前端实现,常用于简单的密钥交换或数字签名。node-forge 功能最为强大,支持 TLS、PKI 和多种加密原语,但体积较大。openpgp 是 OpenPGP 标准(RFC 4880)的完整实现,适用于需要兼容 GPG 的复杂加密场景。tweetnacl 则主打现代、安全且轻量的曲线加密(如 Ed25519, Curve25519),适合对性能和安全性有极高要求的现代应用。
在前端架构中引入加密功能通常是为了解决三个核心问题:数据隐私(防止中间人窃听)、身份验证(确保数据来源)和完整性(防止数据篡改)。然而,JavaScript 加密生态碎片化严重,不同的库解决了不同层面的问题。crypto-js、jsencrypt、node-forge、openpgp 和 tweetnacl 代表了五种完全不同的设计哲学。本文将从算法支持、API 设计、应用场景和安全性四个维度进行深度剖析。
选择加密库的第一步是明确你需要什么算法。不同的库对算法的支持范围差异巨大。
crypto-js 是传统算法的集合体。它主要覆盖哈希(MD5, SHA-1, SHA-256, SHA-512)和对称加密(AES, DES, Rabbit)。它不支持非对称加密(如 RSA 或 ECC)。
// crypto-js: 计算 SHA-256 哈希
import CryptoJS from 'crypto-js';
const hash = CryptoJS.SHA256("message").toString();
// crypto-js: AES 加密
const encrypted = CryptoJS.AES.encrypt("message", "secret key").toString();
jsencrypt 是单一算法专家。它只实现了 RSA 算法(PKCS#1 v1.5 填充),专门用于前端公钥加密、后端私钥解密的场景。
// jsencrypt: RSA 公钥加密
import { JSEncrypt } from 'jsencrypt';
const encryptor = new JSEncrypt();
encryptor.setPublicKey('-----BEGIN PUBLIC KEY-----...');
const encrypted = encryptor.encrypt("sensitive data");
node-forge 是算法百科全书。它支持几乎所有你能想到的算法:RSA, AES, DES, 3DES, RC2, Blowfish, SHA, MD5, HMAC, 甚至包括 ASN.1 解析和 X.509 证书操作。
// node-forge: 生成 RSA 密钥对
import forge from 'node-forge';
const pair = forge.pki.rsa.generateKeyPair({ bits: 2048 });
const pem = forge.pki.publicKeyToPem(pair.publicKey);
openpgp 是协议栈实现。它不仅支持算法(RSA, ECC, AES, SHA),还实现了完整的 OpenPGP 协议层,包括密钥环管理、数据压缩和 Armor 编码。
// openpgp: 使用 OpenPGP 协议加密消息
import * as openpgp from 'openpgp';
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ text: 'Hello' }),
encryptionKeys: publicKey
});
tweetnacl 是现代曲线加密的代表。它摒弃了传统的 RSA 和 AES,专注于 NaCl (Networking and Cryptography library) 定义的现代原语:Curve25519 (密钥交换), Ed25519 (签名), 和 XSalsa20-Poly1305 (认证加密)。
// tweetnacl: 椭圆曲线签名
import nacl from 'tweetnacl';
const keyPair = nacl.sign.keyPair();
const signature = nacl.sign.detached(new TextEncoder().encode("msg"), keyPair.secretKey);
API 的设计直接决定了代码的可维护性和出错概率。
crypto-js 的 API 非常直观,采用链式调用或静态方法,对象自动处理类型转换(如 Hex, Base64)。
// crypto-js: 简洁的链式风格
import CryptoJS from 'crypto-js';
const base64 = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse('data'));
jsencrypt 提供了面向对象的实例化模式,必须先设置密钥才能操作,逻辑清晰但功能单一。
// jsencrypt: 实例化配置
import { JSEncrypt } from 'jsencrypt';
const crypt = new JSEncrypt();
crypt.setPrivateKey('...'); // 设置私钥用于解密
const decrypted = crypt.decrypt(encryptedData);
node-forge 的 API 极其底层且繁琐。它要求开发者手动处理缓冲区(Buffer)、字节数组和 ASN.1 结构。虽然灵活,但极易出错。
// node-forge: 手动处理字节和 ASN.1
import forge from 'node-forge';
const md = forge.md.sha256.create();
md.update('message', 'utf8');
const digest = md.digest().toHex(); // 需要显式转换
openpgp 采用了现代化的 Async/Await 风格,因为 PGP 操作通常涉及复杂的计算,必须异步执行以避免阻塞主线程。
// openpgp: 异步 API
import * as openpgp from 'openpgp';
const { data } = await openpgp.decrypt({
ciphertext: encryptedMessage,
decryptionKeys: privateKey
});
tweetnacl 奉行“极简主义”。它没有配置项,没有对象包装,直接操作 Uint8Array。这要求开发者对二进制数据有清晰的认识,但也保证了极高的性能。
// tweetnacl: 直接操作 Uint8Array
import nacl from 'tweetnacl';
const message = new TextEncoder().encode("secret");
const nonce = nacl.randomBytes(nacl.secretbox.nonceLength);
const box = nacl.secretbox(message, nonce, key);
需求:防止密码在 HTTPS 之前(或即使 HTTPS 被终结)以明文形式到达服务器。
最佳选择:jsencrypt
理由:这是 jsencrypt 的看家本领。后端生成 RSA 密钥对,将公钥发给前端,前端加密密码后发送。由于 RSA 加密速度慢且有限长,仅适合加密短文本(如密码)。
// 使用 jsencrypt 加密登录密码
import { JSEncrypt } from 'jsencrypt';
async function login(username, password, serverPublicKey) {
const encryptor = new JSEncrypt();
encryptor.setPublicKey(serverPublicKey);
const encryptedPass = encryptor.encrypt(password);
return fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ username, password: encryptedPass })
});
}
需求:用户上传文件前,前端计算文件哈希值用于秒传判断或生成缓存 Key。
最佳选择:crypto-js
理由:只需要标准的 SHA-256 算法,无需复杂的密钥管理,crypto-js 处理大文件流(分块哈希)也非常方便。
// 使用 crypto-js 计算文件哈希
import CryptoJS from 'crypto-js';
function getFileHash(fileBlob) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => {
const hash = CryptoJS.SHA256(CryptoJS.lib.WordArray.create(e.target.result));
resolve(hash.toString());
};
reader.readAsArrayBuffer(fileBlob);
});
}
需求:消息在发送前加密,只有接收者能解密,服务器无法查看内容。需要密钥交换和签名。
最佳选择:tweetnacl (配合 tweetnacl-util 处理编码)
理由:现代即时通讯首选椭圆曲线。tweetnacl 提供的 box 接口完美结合了密钥交换(Curve25519)和认证加密(XSalsa20-Poly1305),性能远超 RSA,且安全性更高。
// 使用 tweetnacl 进行安全消息交换
import nacl from 'tweetnacl';
import naclUtil from 'tweetnacl-util';
// 发送方
const receiverPublicKey = naclUtil.decodeBase64(receiverPubKeyStr);
const message = naclUtil.decodeUTF8("Secret message");
const nonce = nacl.randomBytes(nacl.box.nonceLength);
const mySecretKey = naclUtil.decodeBase64(mySecretKeyStr);
const encryptedBox = nacl.box(message, nonce, receiverPublicKey, mySecretKey);
// 发送 nonce 和 encryptedBox 给接收方
需求:Web 端需要读取本地的 GPG 密钥环,加密邮件使其能被 Thunderbird 或 GPG 命令行工具解密。
最佳选择:openpgp
理由:只有 openpgp 完整实现了 RFC 4880 标准。其他库生成的密文无法被标准 GPG 工具识别。
// 使用 openpgp 生成兼容 GPG 的密文
import * as openpgp from 'openpgp';
async function secureEmail(recipientArmoredKey, message) {
const publicKey = await openpgp.readKey({ armoredKey: recipientArmoredKey });
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ text: message }),
encryptionKeys: publicKey,
armor: true // 生成 ASCII armored 格式
});
return encrypted;
}
需求:在浏览器中让用户生成私钥(不离地),并创建证书签名请求(CSR)发送给 CA。
最佳选择:node-forge
理由:这需要操作 X.509 结构和 ASN.1 编码,这是 node-forge 的独有优势领域,其他库均不支持此级别的 PKI 操作。
// node-forge: 创建 CSR
import forge from 'node-forge';
const keys = forge.pki.rsa.generateKeyPair(2048);
const csr = forge.pki.createCertificationRequest();
csr.publicKey = keys.publicKey;
csr.setSubject([{ name: 'commonName', value: 'example.com' }]);
csr.sign(keys.privateKey);
const pem = forge.pki.certificationRequestToPem(csr);
在架构决策中,不仅要考虑“能不能做”,还要考虑“安不安全”。
jsencrypt 常被误用于“隐藏”逻辑,但这只是混淆,不是安全。私钥必须存储在用户可控的地方(如输入密码派生,或存储在本地加密存储中)。crypto-js 早期版本依赖 Math.random(),这在密码学上是不安全的。现代用法应确保其使用 window.crypto.getRandomValues。tweetnacl 和 openpgp 默认正确使用系统熵源。crypto-js 的 MD5 或 SHA-1 进行安全用途(碰撞攻击已可行)。对于非对称加密,尽量避免使用 jsencrypt 处理长文本或高并发场景,RSA 性能瓶颈明显,应优先考虑 tweetnacl 的椭圆曲线方案。node-forge 和 openpgp 体积较大(压缩后仍可能超过 100KB),如果仅用于简单的哈希,会显著影响首屏加载速度。此时 crypto-js 的按需加载模块是更好的选择。| 特性 | crypto-js | jsencrypt | node-forge | openpgp | tweetnacl |
|---|---|---|---|---|---|
| 主要用途 | 哈希 / 对称加密 | RSA 加密 | PKI / 证书 / 全能 | PGP 标准兼容 | 现代曲线加密 |
| 非对称加密 | ❌ 不支持 | ✅ RSA 仅 | ✅ 多种 | ✅ 多种 | ✅ ECC (Ed/Curve) |
| API 复杂度 | 低 | 低 | 高 (底层) | 中 (异步) | 极低 (二进制) |
| 包体积 | 中 | 小 | 大 | 大 | 极小 |
| GPG 兼容 | ❌ | ❌ | ❌ | ✅ 完美 | ❌ |
| 推荐场景 | 密码哈希, 数据指纹 | 登录密码加密 | 证书生成, CSR | 加密邮件, 文件 | 即时通讯, 高安应用 |
如果你的项目只是需要给密码加个哈希,或者做简单的 AES 数据混淆,crypto-js 是最快上手的选择,但请确保只使用 SHA-256 及以上算法。
如果你正在构建一个需要“公钥加密、私钥解密”的登录系统,且不想引入沉重的依赖,jsencrypt 是标准解法,但请记住它仅限于 RSA。
如果你需要处理 X.509 证书、生成 CSR 或在浏览器端模拟完整的 TLS 握手,node-forge 是唯一的选择,尽管它的 API 学习曲线较陡峭。
如果你的业务涉及邮件加密、文件加密且需要与现有的 GPG 基础设施互通,openpgp 是必须的,没有替代品。
最后,如果你正在构建下一代高安全应用(如 Web3 钱包、端到端加密聊天),请拥抱现代密码学,选择 tweetnacl。它强迫你使用更安全的算法和更严谨的数据类型,从架构层面减少了出错的可能。
选择 node-forge 如果你需要在浏览器端实现复杂的 PKI 功能,如生成 CSR、解析 X.509 证书、处理 TLS 握手或操作 ASN.1 结构。它是功能最全面的库,几乎涵盖了所有加密原语。但由于其体积庞大且 API 较为底层,对于只需要简单加密的项目来说,它可能过于笨重。
选择 tweetnacl 如果你追求极致的安全性和现代密码学实践,倾向于使用椭圆曲线算法(如 Ed25519 签名和 Curve25519 密钥交换)。它的设计哲学是“难以误用”,API 极其精简且没有配置选项,非常适合构建高安全性的即时通讯、区块链应用或需要高性能加密的场景。
选择 crypto-js 如果你只需要标准的哈希算法(如 MD5, SHA-256)或简单的对称加密(如 AES)。它非常适合用于计算文件指纹、对密码进行前端哈希预处理,或在非敏感场景下进行数据混淆。它的 API 简单直观,社区使用广泛,但不适合处理复杂的密钥管理或非对称加密需求。
选择 openpgp 如果你的应用场景需要符合 OpenPGP 标准(RFC 4880),例如实现端到端加密邮件、兼容 GPG 密钥环或处理 armored 格式的加密消息。它提供了完整的高层 API 来处理密钥生成、加密、解密和签名。如果只需要基础的 AES 或 RSA 功能而不需要 PGP 协议栈,使用它会引入不必要的复杂度。
选择 jsencrypt 如果你的核心需求是在前端使用 RSA 公钥加密少量数据(如登录密码)并发送给后端解密。它是一个轻量级的单一用途库,专门解决 RSA 在浏览器端的兼容性问题。如果你需要更复杂的证书处理、签名验证或除了 RSA 之外的其他算法,它可能显得功能不足。
A native implementation of TLS (and various other cryptographic tools) in JavaScript.
The Forge software is a fully native implementation of the TLS protocol in JavaScript, a set of cryptography utilities, and a set of tools for developing Web Apps that utilize many network resources.
Forge is fast. Benchmarks against other popular JavaScript cryptography libraries can be found here:
Note: Please see the Security Considerations section before using packaging systems and pre-built files.
Forge uses a CommonJS module structure with a build process for browser bundles. The older 0.6.x branch with standalone files is available but will not be regularly updated.
If you want to use forge with Node.js, it is available through npm:
https://www.npmjs.com/package/node-forge
Installation:
npm install node-forge
You can then use forge as a regular module:
var forge = require('node-forge');
The npm package includes pre-built forge.min.js, forge.all.min.js, and
prime.worker.min.js using the UMD format.
To use it via jsDelivr include this in your html:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/forge.min.js"></script>
To use it via unpkg include this in your html:
<script src="https://unpkg.com/[email protected]/dist/forge.min.js"></script>
The core JavaScript has the following requirements to build and test:
Some special networking features can optionally use a Flash component. See the Flash README for details.
To create single file bundles for use with browsers run the following:
npm install
npm run build
This will create single non-minimized and minimized files that can be included in the browser:
dist/forge.js
dist/forge.min.js
A bundle that adds some utilities and networking support is also available:
dist/forge.all.js
dist/forge.all.min.js
Include the file via:
<script src="YOUR_SCRIPT_PATH/forge.js"></script>
or
<script src="YOUR_SCRIPT_PATH/forge.min.js"></script>
The above bundles will synchronously create a global 'forge' object.
Note: These bundles will not include any WebWorker scripts (eg:
dist/prime.worker.js), so these will need to be accessible from the browser
if any WebWorkers are used.
The build process uses webpack and the config file can be modified to generate a file or files that only contain the parts of forge you need.
Browserify override support is also present in package.json.
npm install
Forge natively runs in a Node.js environment:
npm test
Automated testing is done via Karma. By default it will run the tests with Headless Chrome.
npm run test-karma
Is 'mocha' reporter output too verbose? Other reporters are available. Try 'dots', 'progress', or 'tap'.
npm run test-karma -- --reporters progress
By default webpack is used. Browserify can also be used.
BUNDLER=browserify npm run test-karma
You can also specify one or more browsers to use.
npm run test-karma -- --browsers Chrome,Firefox,Safari,ChromeHeadless
The reporter option and BUNDLER environment variable can also be used.
Testing in a browser uses webpack to combine forge and all tests and then
loading the result in a browser. A simple web server is provided that will
output the HTTP or HTTPS URLs to load. It also will start a simple Flash Policy
Server. Unit tests and older legacy tests are provided. Custom ports can be
used by running node tests/server.js manually.
To run the unit tests in a browser a special forge build is required:
npm run test-build
To run legacy browser based tests the main forge build is required:
npm run build
The tests are run with a custom server that prints out the URLs to use:
npm run test-server
There are some other random tests and benchmarks available in the tests directory.
To perform coverage testing of the unit tests, run the following. The results
will be put in the coverage/ directory. Note that coverage testing can slow
down some tests considerably.
npm install
npm run coverage
Any contributions (eg: PRs) that are accepted will be brought under the same license used by the rest of the Forge project. This license allows Forge to be used under the terms of either the BSD License or the GNU General Public License (GPL) Version 2.
See: LICENSE
If a contribution contains 3rd party source code with its own license, it may retain it, so long as that license is compatible with the Forge license.
If at any time you wish to disable the use of native code, where available,
for particular forge features like its secure random number generator, you
may set the forge.options.usePureJavaScript flag to true. It is
not recommended that you set this flag as native code is typically more
performant and may have stronger security properties. It may be useful to
set this flag to test certain features that you plan to run in environments
that are different from your testing environment.
To disable native code when including forge in the browser:
// run this *after* including the forge script
forge.options.usePureJavaScript = true;
To disable native code when using Node.js:
var forge = require('node-forge');
forge.options.usePureJavaScript = true;
Provides a native javascript client and server-side TLS implementation.
Examples
// create TLS client
var client = forge.tls.createConnection({
server: false,
caStore: /* Array of PEM-formatted certs or a CA store object */,
sessionCache: {},
// supported cipher suites in order of preference
cipherSuites: [
forge.tls.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA,
forge.tls.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA],
virtualHost: 'example.com',
verify: function(connection, verified, depth, certs) {
if(depth === 0) {
var cn = certs[0].subject.getField('CN').value;
if(cn !== 'example.com') {
verified = {
alert: forge.tls.Alert.Description.bad_certificate,
message: 'Certificate common name does not match hostname.'
};
}
}
return verified;
},
connected: function(connection) {
console.log('connected');
// send message to server
connection.prepare(forge.util.encodeUtf8('Hi server!'));
/* NOTE: experimental, start heartbeat retransmission timer
myHeartbeatTimer = setInterval(function() {
connection.prepareHeartbeatRequest(forge.util.createBuffer('1234'));
}, 5*60*1000);*/
},
/* provide a client-side cert if you want
getCertificate: function(connection, hint) {
return myClientCertificate;
},
/* the private key for the client-side cert if provided */
getPrivateKey: function(connection, cert) {
return myClientPrivateKey;
},
tlsDataReady: function(connection) {
// TLS data (encrypted) is ready to be sent to the server
sendToServerSomehow(connection.tlsData.getBytes());
// if you were communicating with the server below, you'd do:
// server.process(connection.tlsData.getBytes());
},
dataReady: function(connection) {
// clear data from the server is ready
console.log('the server sent: ' +
forge.util.decodeUtf8(connection.data.getBytes()));
// close connection
connection.close();
},
/* NOTE: experimental
heartbeatReceived: function(connection, payload) {
// restart retransmission timer, look at payload
clearInterval(myHeartbeatTimer);
myHeartbeatTimer = setInterval(function() {
connection.prepareHeartbeatRequest(forge.util.createBuffer('1234'));
}, 5*60*1000);
payload.getBytes();
},*/
closed: function(connection) {
console.log('disconnected');
},
error: function(connection, error) {
console.log('uh oh', error);
}
});
// start the handshake process
client.handshake();
// when encrypted TLS data is received from the server, process it
client.process(encryptedBytesFromServer);
// create TLS server
var server = forge.tls.createConnection({
server: true,
caStore: /* Array of PEM-formatted certs or a CA store object */,
sessionCache: {},
// supported cipher suites in order of preference
cipherSuites: [
forge.tls.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA,
forge.tls.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA],
// require a client-side certificate if you want
verifyClient: true,
verify: function(connection, verified, depth, certs) {
if(depth === 0) {
var cn = certs[0].subject.getField('CN').value;
if(cn !== 'the-client') {
verified = {
alert: forge.tls.Alert.Description.bad_certificate,
message: 'Certificate common name does not match expected client.'
};
}
}
return verified;
},
connected: function(connection) {
console.log('connected');
// send message to client
connection.prepare(forge.util.encodeUtf8('Hi client!'));
/* NOTE: experimental, start heartbeat retransmission timer
myHeartbeatTimer = setInterval(function() {
connection.prepareHeartbeatRequest(forge.util.createBuffer('1234'));
}, 5*60*1000);*/
},
getCertificate: function(connection, hint) {
return myServerCertificate;
},
getPrivateKey: function(connection, cert) {
return myServerPrivateKey;
},
tlsDataReady: function(connection) {
// TLS data (encrypted) is ready to be sent to the client
sendToClientSomehow(connection.tlsData.getBytes());
// if you were communicating with the client above you'd do:
// client.process(connection.tlsData.getBytes());
},
dataReady: function(connection) {
// clear data from the client is ready
console.log('the client sent: ' +
forge.util.decodeUtf8(connection.data.getBytes()));
// close connection
connection.close();
},
/* NOTE: experimental
heartbeatReceived: function(connection, payload) {
// restart retransmission timer, look at payload
clearInterval(myHeartbeatTimer);
myHeartbeatTimer = setInterval(function() {
connection.prepareHeartbeatRequest(forge.util.createBuffer('1234'));
}, 5*60*1000);
payload.getBytes();
},*/
closed: function(connection) {
console.log('disconnected');
},
error: function(connection, error) {
console.log('uh oh', error);
}
});
// when encrypted TLS data is received from the client, process it
server.process(encryptedBytesFromClient);
Connect to a TLS server using node's net.Socket:
var socket = new net.Socket();
var client = forge.tls.createConnection({
server: false,
verify: function(connection, verified, depth, certs) {
// skip verification for testing
console.log('[tls] server certificate verified');
return true;
},
connected: function(connection) {
console.log('[tls] connected');
// prepare some data to send (note that the string is interpreted as
// 'binary' encoded, which works for HTTP which only uses ASCII, use
// forge.util.encodeUtf8(str) otherwise
client.prepare('GET / HTTP/1.0\r\n\r\n');
},
tlsDataReady: function(connection) {
// encrypted data is ready to be sent to the server
var data = connection.tlsData.getBytes();
socket.write(data, 'binary'); // encoding should be 'binary'
},
dataReady: function(connection) {
// clear data from the server is ready
var data = connection.data.getBytes();
console.log('[tls] data received from the server: ' + data);
},
closed: function() {
console.log('[tls] disconnected');
},
error: function(connection, error) {
console.log('[tls] error', error);
}
});
socket.on('connect', function() {
console.log('[socket] connected');
client.handshake();
});
socket.on('data', function(data) {
client.process(data.toString('binary')); // encoding should be 'binary'
});
socket.on('end', function() {
console.log('[socket] disconnected');
});
// connect to google.com
socket.connect(443, 'google.com');
// or connect to gmail's imap server (but don't send the HTTP header above)
//socket.connect(993, 'imap.gmail.com');
Provides a native JavaScript mini-implementation of an http client that uses pooled sockets.
Examples
// create an HTTP GET request
var request = forge.http.createRequest({method: 'GET', path: url.path});
// send the request somewhere
sendSomehow(request.toString());
// receive response
var buffer = forge.util.createBuffer();
var response = forge.http.createResponse();
var someAsyncDataHandler = function(bytes) {
if(!response.bodyReceived) {
buffer.putBytes(bytes);
if(!response.headerReceived) {
if(response.readHeader(buffer)) {
console.log('HTTP response header: ' + response.toString());
}
}
if(response.headerReceived && !response.bodyReceived) {
if(response.readBody(buffer)) {
console.log('HTTP response body: ' + response.body);
}
}
}
};
Provides some SSH utility functions.
Examples
// encodes (and optionally encrypts) a private RSA key as a Putty PPK file
forge.ssh.privateKeyToPutty(privateKey, passphrase, comment);
// encodes a public RSA key as an OpenSSH file
forge.ssh.publicKeyToOpenSSH(key, comment);
// encodes a private RSA key as an OpenSSH file
forge.ssh.privateKeyToOpenSSH(privateKey, passphrase);
// gets the SSH public key fingerprint in a byte buffer
forge.ssh.getPublicKeyFingerprint(key);
// gets a hex-encoded, colon-delimited SSH public key fingerprint
forge.ssh.getPublicKeyFingerprint(key, {encoding: 'hex', delimiter: ':'});
Provides an XmlHttpRequest implementation using forge.http as a backend.
Examples
// TODO
Provides an interface to create and use raw sockets provided via Flash.
Examples
// TODO
Provides a basic API for block encryption and decryption. There is built-in support for the ciphers: AES, 3DES, and DES, and for the modes of operation: ECB, CBC, CFB, OFB, CTR, and GCM.
These algorithms are currently supported:
When using an AES algorithm, the key size will determine whether AES-128, AES-192, or AES-256 is used (all are supported). When a DES algorithm is used, the key size will determine whether 3DES or regular DES is used. Use a 3DES algorithm to enforce Triple-DES.
Examples
// generate a random key and IV
// Note: a key size of 16 bytes will use AES-128, 24 => AES-192, 32 => AES-256
var key = forge.random.getBytesSync(16);
var iv = forge.random.getBytesSync(16);
/* alternatively, generate a password-based 16-byte key
var salt = forge.random.getBytesSync(128);
var key = forge.pkcs5.pbkdf2('password', salt, numIterations, 16);
*/
// encrypt some bytes using CBC mode
// (other modes include: ECB, CFB, OFB, CTR, and GCM)
// Note: CBC and ECB modes use PKCS#7 padding as default
var cipher = forge.cipher.createCipher('AES-CBC', key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(someBytes));
cipher.finish();
var encrypted = cipher.output;
// outputs encrypted hex
console.log(encrypted.toHex());
// decrypt some bytes using CBC mode
// (other modes include: CFB, OFB, CTR, and GCM)
var decipher = forge.cipher.createDecipher('AES-CBC', key);
decipher.start({iv: iv});
decipher.update(encrypted);
var result = decipher.finish(); // check 'result' for true/false
// outputs decrypted hex
console.log(decipher.output.toHex());
// decrypt bytes using CBC mode and streaming
// Performance can suffer for large multi-MB inputs due to buffer
// manipulations. Stream processing in chunks can offer significant
// improvement. CPU intensive update() calls could also be performed with
// setImmediate/setTimeout to avoid blocking the main browser UI thread (not
// shown here). Optimal block size depends on the JavaScript VM and other
// factors. Encryption can use a simple technique for increased performance.
var encryptedBytes = encrypted.bytes();
var decipher = forge.cipher.createDecipher('AES-CBC', key);
decipher.start({iv: iv});
var length = encryptedBytes.length;
var chunkSize = 1024 * 64;
var index = 0;
var decrypted = '';
do {
decrypted += decipher.output.getBytes();
var buf = forge.util.createBuffer(encryptedBytes.substr(index, chunkSize));
decipher.update(buf);
index += chunkSize;
} while(index < length);
var result = decipher.finish();
assert(result);
decrypted += decipher.output.getBytes();
console.log(forge.util.bytesToHex(decrypted));
// encrypt some bytes using GCM mode
var cipher = forge.cipher.createCipher('AES-GCM', key);
cipher.start({
iv: iv, // should be a 12-byte binary-encoded string or byte buffer
additionalData: 'binary-encoded string', // optional
tagLength: 128 // optional, defaults to 128 bits
});
cipher.update(forge.util.createBuffer(someBytes));
cipher.finish();
var encrypted = cipher.output;
var tag = cipher.mode.tag;
// outputs encrypted hex
console.log(encrypted.toHex());
// outputs authentication tag
console.log(tag.toHex());
// decrypt some bytes using GCM mode
var decipher = forge.cipher.createDecipher('AES-GCM', key);
decipher.start({
iv: iv,
additionalData: 'binary-encoded string', // optional
tagLength: 128, // optional, defaults to 128 bits
tag: tag // authentication tag from encryption
});
decipher.update(encrypted);
var pass = decipher.finish();
// pass is false if there was a failure (eg: authentication tag didn't match)
if(pass) {
// outputs decrypted hex
console.log(decipher.output.toHex());
}
Using forge in Node.js to match openssl's "enc" command line tool (Note: OpenSSL "enc" uses a non-standard file format with a custom key derivation function and a fixed iteration count of 1, which some consider less secure than alternatives such as OpenPGP/GnuPG):
var forge = require('node-forge');
var fs = require('fs');
// openssl enc -des3 -in input.txt -out input.enc
function encrypt(password) {
var input = fs.readFileSync('input.txt', {encoding: 'binary'});
// 3DES key and IV sizes
var keySize = 24;
var ivSize = 8;
// get derived bytes
// Notes:
// 1. If using an alternative hash (eg: "-md sha1") pass
// "forge.md.sha1.create()" as the final parameter.
// 2. If using "-nosalt", set salt to null.
var salt = forge.random.getBytesSync(8);
// var md = forge.md.sha1.create(); // "-md sha1"
var derivedBytes = forge.pbe.opensslDeriveBytes(
password, salt, keySize + ivSize/*, md*/);
var buffer = forge.util.createBuffer(derivedBytes);
var key = buffer.getBytes(keySize);
var iv = buffer.getBytes(ivSize);
var cipher = forge.cipher.createCipher('3DES-CBC', key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(input, 'binary'));
cipher.finish();
var output = forge.util.createBuffer();
// if using a salt, prepend this to the output:
if(salt !== null) {
output.putBytes('Salted__'); // (add to match openssl tool output)
output.putBytes(salt);
}
output.putBuffer(cipher.output);
fs.writeFileSync('input.enc', output.getBytes(), {encoding: 'binary'});
}
// openssl enc -d -des3 -in input.enc -out input.dec.txt
function decrypt(password) {
var input = fs.readFileSync('input.enc', {encoding: 'binary'});
// parse salt from input
input = forge.util.createBuffer(input, 'binary');
// skip "Salted__" (if known to be present)
input.getBytes('Salted__'.length);
// read 8-byte salt
var salt = input.getBytes(8);
// Note: if using "-nosalt", skip above parsing and use
// var salt = null;
// 3DES key and IV sizes
var keySize = 24;
var ivSize = 8;
var derivedBytes = forge.pbe.opensslDeriveBytes(
password, salt, keySize + ivSize);
var buffer = forge.util.createBuffer(derivedBytes);
var key = buffer.getBytes(keySize);
var iv = buffer.getBytes(ivSize);
var decipher = forge.cipher.createDecipher('3DES-CBC', key);
decipher.start({iv: iv});
decipher.update(input);
var result = decipher.finish(); // check 'result' for true/false
fs.writeFileSync(
'input.dec.txt', decipher.output.getBytes(), {encoding: 'binary'});
}
Provides AES encryption and decryption in CBC, CFB, OFB, CTR, and GCM modes. See CIPHER for examples.
Provides 3DES and DES encryption and decryption in ECB and CBC modes. See CIPHER for examples.
Examples
// generate a random key and IV
var key = forge.random.getBytesSync(16);
var iv = forge.random.getBytesSync(8);
// encrypt some bytes
var cipher = forge.rc2.createEncryptionCipher(key);
cipher.start(iv);
cipher.update(forge.util.createBuffer(someBytes));
cipher.finish();
var encrypted = cipher.output;
// outputs encrypted hex
console.log(encrypted.toHex());
// decrypt some bytes
var cipher = forge.rc2.createDecryptionCipher(key);
cipher.start(iv);
cipher.update(encrypted);
cipher.finish();
// outputs decrypted hex
console.log(cipher.output.toHex());
Provides X.509 certificate support, ED25519 key generation and signing/verifying, and RSA public and private key encoding, decoding, encryption/decryption, and signing/verifying.
Special thanks to TweetNaCl.js for providing the bulk of the implementation.
Examples
var ed25519 = forge.pki.ed25519;
// generate a random ED25519 keypair
var keypair = ed25519.generateKeyPair();
// `keypair.publicKey` is a node.js Buffer or Uint8Array
// `keypair.privateKey` is a node.js Buffer or Uint8Array
// generate a random ED25519 keypair based on a random 32-byte seed
var seed = forge.random.getBytesSync(32);
var keypair = ed25519.generateKeyPair({seed: seed});
// generate a random ED25519 keypair based on a "password" 32-byte seed
var password = 'Mai9ohgh6ahxee0jutheew0pungoozil';
var seed = new forge.util.ByteBuffer(password, 'utf8');
var keypair = ed25519.generateKeyPair({seed: seed});
// sign a UTF-8 message
var signature = ED25519.sign({
message: 'test',
// also accepts `binary` if you want to pass a binary string
encoding: 'utf8',
// node.js Buffer, Uint8Array, forge ByteBuffer, binary string
privateKey: privateKey
});
// `signature` is a node.js Buffer or Uint8Array
// sign a message passed as a buffer
var signature = ED25519.sign({
// also accepts a forge ByteBuffer or Uint8Array
message: Buffer.from('test', 'utf8'),
privateKey: privateKey
});
// sign a message digest (shorter "message" == better performance)
var md = forge.md.sha256.create();
md.update('test', 'utf8');
var signature = ED25519.sign({
md: md,
privateKey: privateKey
});
// verify a signature on a UTF-8 message
var verified = ED25519.verify({
message: 'test',
encoding: 'utf8',
// node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
signature: signature,
// node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
publicKey: publicKey
});
// `verified` is true/false
// sign a message passed as a buffer
var verified = ED25519.verify({
// also accepts a forge ByteBuffer or Uint8Array
message: Buffer.from('test', 'utf8'),
// node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
signature: signature,
// node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
publicKey: publicKey
});
// verify a signature on a message digest
var md = forge.md.sha256.create();
md.update('test', 'utf8');
var verified = ED25519.verify({
md: md,
// node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
signature: signature,
// node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
publicKey: publicKey
});
Examples
var rsa = forge.pki.rsa;
// generate an RSA key pair synchronously
// *NOT RECOMMENDED*: Can be significantly slower than async and may block
// JavaScript execution. Will use native Node.js 10.12.0+ API if possible.
var keypair = rsa.generateKeyPair({bits: 2048, e: 0x10001});
// generate an RSA key pair asynchronously (uses web workers if available)
// use workers: -1 to run a fast core estimator to optimize # of workers
// *RECOMMENDED*: Can be significantly faster than sync. Will use native
// Node.js 10.12.0+ or WebCrypto API if possible.
rsa.generateKeyPair({bits: 2048, workers: 2}, function(err, keypair) {
// keypair.privateKey, keypair.publicKey
});
// generate an RSA key pair in steps that attempt to run for a specified period
// of time on the main JS thread
var state = rsa.createKeyPairGenerationState(2048, 0x10001);
var step = function() {
// run for 100 ms
if(!rsa.stepKeyPairGenerationState(state, 100)) {
setTimeout(step, 1);
}
else {
// done, turn off progress indicator, use state.keys
}
};
// turn on progress indicator, schedule generation to run
setTimeout(step);
// sign data with a private key and output DigestInfo DER-encoded bytes
// (defaults to RSASSA PKCS#1 v1.5)
var md = forge.md.sha1.create();
md.update('sign this', 'utf8');
var signature = privateKey.sign(md);
// verify data with a public key
// (defaults to RSASSA PKCS#1 v1.5)
var verified = publicKey.verify(md.digest().bytes(), signature);
// sign data using RSASSA-PSS where PSS uses a SHA-1 hash, a SHA-1 based
// masking function MGF1, and a 20 byte salt
var md = forge.md.sha1.create();
md.update('sign this', 'utf8');
var pss = forge.pss.create({
md: forge.md.sha1.create(),
mgf: forge.mgf.mgf1.create(forge.md.sha1.create()),
saltLength: 20
// optionally pass 'prng' with a custom PRNG implementation
// optionalls pass 'salt' with a forge.util.ByteBuffer w/custom salt
});
var signature = privateKey.sign(md, pss);
// verify RSASSA-PSS signature
var pss = forge.pss.create({
md: forge.md.sha1.create(),
mgf: forge.mgf.mgf1.create(forge.md.sha1.create()),
saltLength: 20
// optionally pass 'prng' with a custom PRNG implementation
});
var md = forge.md.sha1.create();
md.update('sign this', 'utf8');
publicKey.verify(md.digest().getBytes(), signature, pss);
// encrypt data with a public key (defaults to RSAES PKCS#1 v1.5)
var encrypted = publicKey.encrypt(bytes);
// decrypt data with a private key (defaults to RSAES PKCS#1 v1.5)
var decrypted = privateKey.decrypt(encrypted);
// encrypt data with a public key using RSAES PKCS#1 v1.5
var encrypted = publicKey.encrypt(bytes, 'RSAES-PKCS1-V1_5');
// decrypt data with a private key using RSAES PKCS#1 v1.5
var decrypted = privateKey.decrypt(encrypted, 'RSAES-PKCS1-V1_5');
// encrypt data with a public key using RSAES-OAEP
var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP');
// decrypt data with a private key using RSAES-OAEP
var decrypted = privateKey.decrypt(encrypted, 'RSA-OAEP');
// encrypt data with a public key using RSAES-OAEP/SHA-256
var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP', {
md: forge.md.sha256.create()
});
// decrypt data with a private key using RSAES-OAEP/SHA-256
var decrypted = privateKey.decrypt(encrypted, 'RSA-OAEP', {
md: forge.md.sha256.create()
});
// encrypt data with a public key using RSAES-OAEP/SHA-256/MGF1-SHA-1
// compatible with Java's RSA/ECB/OAEPWithSHA-256AndMGF1Padding
var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP', {
md: forge.md.sha256.create(),
mgf1: {
md: forge.md.sha1.create()
}
});
// decrypt data with a private key using RSAES-OAEP/SHA-256/MGF1-SHA-1
// compatible with Java's RSA/ECB/OAEPWithSHA-256AndMGF1Padding
var decrypted = privateKey.decrypt(encrypted, 'RSA-OAEP', {
md: forge.md.sha256.create(),
mgf1: {
md: forge.md.sha1.create()
}
});
Examples
// generate an RSA key pair asynchronously (uses web workers if available)
// use workers: -1 to run a fast core estimator to optimize # of workers
forge.rsa.generateKeyPair({bits: 2048, workers: -1}, function(err, keypair) {
// keypair.privateKey, keypair.publicKey
});
// generate and encapsulate a 16-byte secret key
var kdf1 = new forge.kem.kdf1(forge.md.sha1.create());
var kem = forge.kem.rsa.create(kdf1);
var result = kem.encrypt(keypair.publicKey, 16);
// result has 'encapsulation' and 'key'
// encrypt some bytes
var iv = forge.random.getBytesSync(12);
var someBytes = 'hello world!';
var cipher = forge.cipher.createCipher('AES-GCM', result.key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(someBytes));
cipher.finish();
var encrypted = cipher.output.getBytes();
var tag = cipher.mode.tag.getBytes();
// send 'encrypted', 'iv', 'tag', and result.encapsulation to recipient
// decrypt encapsulated 16-byte secret key
var kdf1 = new forge.kem.kdf1(forge.md.sha1.create());
var kem = forge.kem.rsa.create(kdf1);
var key = kem.decrypt(keypair.privateKey, result.encapsulation, 16);
// decrypt some bytes
var decipher = forge.cipher.createDecipher('AES-GCM', key);
decipher.start({iv: iv, tag: tag});
decipher.update(forge.util.createBuffer(encrypted));
var pass = decipher.finish();
// pass is false if there was a failure (eg: authentication tag didn't match)
if(pass) {
// outputs 'hello world!'
console.log(decipher.output.getBytes());
}
Examples
var pki = forge.pki;
// convert a PEM-formatted public key to a Forge public key
var publicKey = pki.publicKeyFromPem(pem);
// convert a Forge public key to PEM-format
var pem = pki.publicKeyToPem(publicKey);
// convert an ASN.1 SubjectPublicKeyInfo to a Forge public key
var publicKey = pki.publicKeyFromAsn1(subjectPublicKeyInfo);
// convert a Forge public key to an ASN.1 SubjectPublicKeyInfo
var subjectPublicKeyInfo = pki.publicKeyToAsn1(publicKey);
// gets a SHA-1 RSAPublicKey fingerprint a byte buffer
pki.getPublicKeyFingerprint(key);
// gets a SHA-1 SubjectPublicKeyInfo fingerprint a byte buffer
pki.getPublicKeyFingerprint(key, {type: 'SubjectPublicKeyInfo'});
// gets a hex-encoded, colon-delimited SHA-1 RSAPublicKey public key fingerprint
pki.getPublicKeyFingerprint(key, {encoding: 'hex', delimiter: ':'});
// gets a hex-encoded, colon-delimited SHA-1 SubjectPublicKeyInfo public key fingerprint
pki.getPublicKeyFingerprint(key, {
type: 'SubjectPublicKeyInfo',
encoding: 'hex',
delimiter: ':'
});
// gets a hex-encoded, colon-delimited MD5 RSAPublicKey public key fingerprint
pki.getPublicKeyFingerprint(key, {
md: forge.md.md5.create(),
encoding: 'hex',
delimiter: ':'
});
// creates a CA store
var caStore = pki.createCaStore([/* PEM-encoded cert */, ...]);
// add a certificate to the CA store
caStore.addCertificate(certObjectOrPemString);
// gets the issuer (its certificate) for the given certificate
var issuerCert = caStore.getIssuer(subjectCert);
// verifies a certificate chain against a CA store
pki.verifyCertificateChain(caStore, chain, customVerifyCallback);
// signs a certificate using the given private key
cert.sign(privateKey);
// signs a certificate using SHA-256 instead of SHA-1
cert.sign(privateKey, forge.md.sha256.create());
// verifies an issued certificate using the certificates public key
var verified = issuer.verify(issued);
// generate a keypair and create an X.509v3 certificate
var keys = pki.rsa.generateKeyPair(2048);
var cert = pki.createCertificate();
cert.publicKey = keys.publicKey;
// alternatively set public key from a csr
//cert.publicKey = csr.publicKey;
// NOTE: serialNumber is the hex encoded value of an ASN.1 INTEGER.
// Conforming CAs should ensure serialNumber is:
// - no more than 20 octets
// - non-negative (prefix a '00' if your value starts with a '1' bit)
cert.serialNumber = '01';
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
var attrs = [{
name: 'commonName',
value: 'example.org'
}, {
name: 'countryName',
value: 'US'
}, {
shortName: 'ST',
value: 'Virginia'
}, {
name: 'localityName',
value: 'Blacksburg'
}, {
name: 'organizationName',
value: 'Test'
}, {
shortName: 'OU',
value: 'Test'
}];
cert.setSubject(attrs);
// alternatively set subject from a csr
//cert.setSubject(csr.subject.attributes);
cert.setIssuer(attrs);
cert.setExtensions([{
name: 'basicConstraints',
cA: true
}, {
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true
}, {
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: true,
emailProtection: true,
timeStamping: true
}, {
name: 'nsCertType',
client: true,
server: true,
email: true,
objsign: true,
sslCA: true,
emailCA: true,
objCA: true
}, {
name: 'subjectAltName',
altNames: [{
type: 6, // URI
value: 'http://example.org/webid#me'
}, {
type: 7, // IP
ip: '127.0.0.1'
}]
}, {
name: 'subjectKeyIdentifier'
}]);
/* alternatively set extensions from a csr
var extensions = csr.getAttribute({name: 'extensionRequest'}).extensions;
// optionally add more extensions
extensions.push.apply(extensions, [{
name: 'basicConstraints',
cA: true
}, {
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true
}]);
cert.setExtensions(extensions);
*/
// self-sign certificate
cert.sign(keys.privateKey);
// convert a Forge certificate to PEM
var pem = pki.certificateToPem(cert);
// convert a Forge certificate from PEM
var cert = pki.certificateFromPem(pem);
// convert an ASN.1 X.509x3 object to a Forge certificate
var cert = pki.certificateFromAsn1(obj);
// convert a Forge certificate to an ASN.1 X.509v3 object
var asn1Cert = pki.certificateToAsn1(cert);
Provides the password-based key-derivation function from PKCS#5.
Examples
// generate a password-based 16-byte key
// note an optional message digest can be passed as the final parameter
var salt = forge.random.getBytesSync(128);
var derivedKey = forge.pkcs5.pbkdf2('password', salt, numIterations, 16);
// generate key asynchronously
// note an optional message digest can be passed before the callback
forge.pkcs5.pbkdf2('password', salt, numIterations, 16, function(err, derivedKey) {
// do something w/derivedKey
});
Provides cryptographically protected messages from PKCS#7.
Examples
// convert a message from PEM
var p7 = forge.pkcs7.messageFromPem(pem);
// look at p7.recipients
// find a recipient by the issuer of a certificate
var recipient = p7.findRecipient(cert);
// decrypt
p7.decrypt(p7.recipients[0], privateKey);
// create a p7 enveloped message
var p7 = forge.pkcs7.createEnvelopedData();
// add a recipient
var cert = forge.pki.certificateFromPem(certPem);
p7.addRecipient(cert);
// set content
p7.content = forge.util.createBuffer('Hello');
// encrypt
p7.encrypt();
// convert message to PEM
var pem = forge.pkcs7.messageToPem(p7);
// create a degenerate PKCS#7 certificate container
// (CRLs not currently supported, only certificates)
var p7 = forge.pkcs7.createSignedData();
p7.addCertificate(certOrCertPem1);
p7.addCertificate(certOrCertPem2);
var pem = forge.pkcs7.messageToPem(p7);
// create PKCS#7 signed data with authenticatedAttributes
// attributes include: PKCS#9 content-type, message-digest, and signing-time
var p7 = forge.pkcs7.createSignedData();
p7.content = forge.util.createBuffer('Some content to be signed.', 'utf8');
p7.addCertificate(certOrCertPem);
p7.addSigner({
key: privateKeyAssociatedWithCert,
certificate: certOrCertPem,
digestAlgorithm: forge.pki.oids.sha256,
authenticatedAttributes: [{
type: forge.pki.oids.contentType,
value: forge.pki.oids.data
}, {
type: forge.pki.oids.messageDigest
// value will be auto-populated at signing time
}, {
type: forge.pki.oids.signingTime,
// value can also be auto-populated at signing time
value: new Date()
}]
});
p7.sign();
var pem = forge.pkcs7.messageToPem(p7);
// PKCS#7 Sign in detached mode.
// Includes the signature and certificate without the signed data.
p7.sign({detached: true});
Examples
var pki = forge.pki;
// convert a PEM-formatted private key to a Forge private key
var privateKey = pki.privateKeyFromPem(pem);
// convert a Forge private key to PEM-format
var pem = pki.privateKeyToPem(privateKey);
// convert an ASN.1 PrivateKeyInfo or RSAPrivateKey to a Forge private key
var privateKey = pki.privateKeyFromAsn1(rsaPrivateKey);
// convert a Forge private key to an ASN.1 RSAPrivateKey
var rsaPrivateKey = pki.privateKeyToAsn1(privateKey);
// wrap an RSAPrivateKey ASN.1 object in a PKCS#8 ASN.1 PrivateKeyInfo
var privateKeyInfo = pki.wrapRsaPrivateKey(rsaPrivateKey);
// convert a PKCS#8 ASN.1 PrivateKeyInfo to PEM
var pem = pki.privateKeyInfoToPem(privateKeyInfo);
// encrypts a PrivateKeyInfo using a custom password and
// outputs an EncryptedPrivateKeyInfo
var encryptedPrivateKeyInfo = pki.encryptPrivateKeyInfo(
privateKeyInfo, 'myCustomPasswordHere', {
algorithm: 'aes256', // 'aes128', 'aes192', 'aes256', '3des'
});
// decrypts an ASN.1 EncryptedPrivateKeyInfo that was encrypted
// with a custom password
var privateKeyInfo = pki.decryptPrivateKeyInfo(
encryptedPrivateKeyInfo, 'myCustomPasswordHere');
// converts an EncryptedPrivateKeyInfo to PEM
var pem = pki.encryptedPrivateKeyToPem(encryptedPrivateKeyInfo);
// converts a PEM-encoded EncryptedPrivateKeyInfo to ASN.1 format
var encryptedPrivateKeyInfo = pki.encryptedPrivateKeyFromPem(pem);
// wraps and encrypts a Forge private key and outputs it in PEM format
var pem = pki.encryptRsaPrivateKey(privateKey, 'password');
// encrypts a Forge private key and outputs it in PEM format using OpenSSL's
// proprietary legacy format + encapsulated PEM headers (DEK-Info)
var pem = pki.encryptRsaPrivateKey(privateKey, 'password', {legacy: true});
// decrypts a PEM-formatted, encrypted private key
var privateKey = pki.decryptRsaPrivateKey(pem, 'password');
// sets an RSA public key from a private key
var publicKey = pki.setRsaPublicKey(privateKey.n, privateKey.e);
Provides certification requests or certificate signing requests (CSR) from PKCS#10.
Examples
// generate a key pair
var keys = forge.pki.rsa.generateKeyPair(2048);
// create a certification request (CSR)
var csr = forge.pki.createCertificationRequest();
csr.publicKey = keys.publicKey;
csr.setSubject([{
name: 'commonName',
value: 'example.org'
}, {
name: 'countryName',
value: 'US'
}, {
shortName: 'ST',
value: 'Virginia'
}, {
name: 'localityName',
value: 'Blacksburg'
}, {
name: 'organizationName',
value: 'Test'
}, {
shortName: 'OU',
value: 'Test'
}]);
// set (optional) attributes
csr.setAttributes([{
name: 'challengePassword',
value: 'password'
}, {
name: 'unstructuredName',
value: 'My Company, Inc.'
}, {
name: 'extensionRequest',
extensions: [{
name: 'subjectAltName',
altNames: [{
// 2 is DNS type
type: 2,
value: 'test.domain.com'
}, {
type: 2,
value: 'other.domain.com',
}, {
type: 2,
value: 'www.domain.net'
}]
}]
}]);
// sign certification request
csr.sign(keys.privateKey);
// verify certification request
var verified = csr.verify();
// convert certification request to PEM-format
var pem = forge.pki.certificationRequestToPem(csr);
// convert a Forge certification request from PEM-format
var csr = forge.pki.certificationRequestFromPem(pem);
// get an attribute
csr.getAttribute({name: 'challengePassword'});
// get extensions array
csr.getAttribute({name: 'extensionRequest'}).extensions;
Provides the cryptographic archive file format from PKCS#12.
Note for Chrome/Firefox/iOS/similar users: If you have trouble importing
a PKCS#12 container, try using the TripleDES algorithm. It can be passed
to forge.pkcs12.toPkcs12Asn1 using the {algorithm: '3des'} option.
Examples
// decode p12 from base64
var p12Der = forge.util.decode64(p12b64);
// get p12 as ASN.1 object
var p12Asn1 = forge.asn1.fromDer(p12Der);
// decrypt p12 using the password 'password'
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, 'password');
// decrypt p12 using non-strict parsing mode (resolves some ASN.1 parse errors)
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, 'password');
// decrypt p12 using literally no password (eg: Mac OS X/apple push)
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1);
// decrypt p12 using an "empty" password (eg: OpenSSL with no password input)
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, '');
// p12.safeContents is an array of safe contents, each of
// which contains an array of safeBags
// get bags by friendlyName
var bags = p12.getBags({friendlyName: 'test'});
// bags are key'd by attribute type (here "friendlyName")
// and the key values are an array of matching objects
var cert = bags.friendlyName[0];
// get bags by localKeyId
var bags = p12.getBags({localKeyId: buffer});
// bags are key'd by attribute type (here "localKeyId")
// and the key values are an array of matching objects
var cert = bags.localKeyId[0];
// get bags by localKeyId (input in hex)
var bags = p12.getBags({localKeyIdHex: '7b59377ff142d0be4565e9ac3d396c01401cd879'});
// bags are key'd by attribute type (here "localKeyId", *not* "localKeyIdHex")
// and the key values are an array of matching objects
var cert = bags.localKeyId[0];
// get bags by type
var bags = p12.getBags({bagType: forge.pki.oids.certBag});
// bags are key'd by bagType and each bagType key's value
// is an array of matches (in this case, certificate objects)
var cert = bags[forge.pki.oids.certBag][0];
// get bags by friendlyName and filter on bag type
var bags = p12.getBags({
friendlyName: 'test',
bagType: forge.pki.oids.certBag
});
// get key bags
var bags = p12.getBags({bagType: forge.pki.oids.keyBag});
// get key
var bag = bags[forge.pki.oids.keyBag][0];
var key = bag.key;
// if the key is in a format unrecognized by forge then
// bag.key will be `null`, use bag.asn1 to get the ASN.1
// representation of the key
if(bag.key === null) {
var keyAsn1 = bag.asn1;
// can now convert back to DER/PEM/etc for export
}
// generate a p12 using AES (default)
var p12Asn1 = forge.pkcs12.toPkcs12Asn1(
privateKey, certificateChain, 'password');
// generate a p12 that can be imported by Chrome/Firefox/iOS
// (requires the use of Triple DES instead of AES)
var p12Asn1 = forge.pkcs12.toPkcs12Asn1(
privateKey, certificateChain, 'password',
{algorithm: '3des'});
// base64-encode p12
var p12Der = forge.asn1.toDer(p12Asn1).getBytes();
var p12b64 = forge.util.encode64(p12Der);
// create download link for p12
var a = document.createElement('a');
a.download = 'example.p12';
a.setAttribute('href', 'data:application/x-pkcs12;base64,' + p12b64);
a.appendChild(document.createTextNode('Download'));
Provides ASN.1 DER encoding and decoding.
Examples
var asn1 = forge.asn1;
// create a SubjectPublicKeyInfo
var subjectPublicKeyInfo =
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// AlgorithmIdentifier
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// algorithm
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(pki.oids['rsaEncryption']).getBytes()),
// parameters (null)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
]),
// subjectPublicKey
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, [
// RSAPublicKey
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// modulus (n)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
_bnToBytes(key.n)),
// publicExponent (e)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
_bnToBytes(key.e))
])
])
]);
// serialize an ASN.1 object to DER format
var derBuffer = asn1.toDer(subjectPublicKeyInfo);
// deserialize to an ASN.1 object from a byte buffer filled with DER data
var object = asn1.fromDer(derBuffer);
// convert an OID dot-separated string to a byte buffer
var derOidBuffer = asn1.oidToDer('1.2.840.113549.1.1.5');
// convert a byte buffer with a DER-encoded OID to a dot-separated string
console.log(asn1.derToOid(derOidBuffer));
// output: 1.2.840.113549.1.1.5
// validates that an ASN.1 object matches a particular ASN.1 structure and
// captures data of interest from that structure for easy access
var publicKeyValidator = {
name: 'SubjectPublicKeyInfo',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
captureAsn1: 'subjectPublicKeyInfo',
value: [{
name: 'SubjectPublicKeyInfo.AlgorithmIdentifier',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'AlgorithmIdentifier.algorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'publicKeyOid'
}]
}, {
// subjectPublicKey
name: 'SubjectPublicKeyInfo.subjectPublicKey',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.BITSTRING,
constructed: false,
value: [{
// RSAPublicKey
name: 'SubjectPublicKeyInfo.subjectPublicKey.RSAPublicKey',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
optional: true,
captureAsn1: 'rsaPublicKey'
}]
}]
};
var capture = {};
var errors = [];
if(!asn1.validate(
publicKeyValidator, subjectPublicKeyInfo, validator, capture, errors)) {
throw 'ASN.1 object is not a SubjectPublicKeyInfo.';
}
// capture.subjectPublicKeyInfo contains the full ASN.1 object
// capture.rsaPublicKey contains the full ASN.1 object for the RSA public key
// capture.publicKeyOid only contains the value for the OID
var oid = asn1.derToOid(capture.publicKeyOid);
if(oid !== pki.oids['rsaEncryption']) {
throw 'Unsupported OID.';
}
// pretty print an ASN.1 object to a string for debugging purposes
asn1.prettyPrint(object);
Provides SHA-1 message digests.
Examples
var md = forge.md.sha1.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12
Provides SHA-256 message digests.
Examples
var md = forge.md.sha256.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592
Provides SHA-384 message digests.
Examples
var md = forge.md.sha384.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1
Provides SHA-512 message digests.
Examples
// SHA-512
var md = forge.md.sha512.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: 07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6
// SHA-512/224
var md = forge.md.sha512.sha224.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: 944cd2847fb54558d4775db0485a50003111c8e5daa63fe722c6aa37
// SHA-512/256
var md = forge.md.sha512.sha256.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: dd9d67b371519c339ed8dbd25af90e976a1eeefd4ad3d889005e532fc5bef04d
Examples
var md = forge.md.md5.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: 9e107d9d372bb6826bd81d3542a419d6
Provides HMAC w/any supported message digest algorithm.
Examples
var hmac = forge.hmac.create();
hmac.start('sha1', 'Jefe');
hmac.update('what do ya want for nothing?');
console.log(hmac.digest().toHex());
// output: effcdf6ae5eb2fa2d27416d5f184df9c259a7c79
Provides an API for generating large, random, probable primes.
Examples
// generate a random prime on the main JS thread
var bits = 1024;
forge.prime.generateProbablePrime(bits, function(err, num) {
console.log('random prime', num.toString(16));
});
// generate a random prime using Web Workers (if available, otherwise
// falls back to the main thread)
var bits = 1024;
var options = {
algorithm: {
name: 'PRIMEINC',
workers: -1 // auto-optimize # of workers
}
};
forge.prime.generateProbablePrime(bits, options, function(err, num) {
console.log('random prime', num.toString(16));
});
Provides a Fortuna-based cryptographically-secure pseudo-random number generator, to be used with a cryptographic function backend, e.g. AES. An implementation using AES as a backend is provided. An API for collecting entropy is given, though if window.crypto.getRandomValues is available, it will be used automatically.
Examples
// get some random bytes synchronously
var bytes = forge.random.getBytesSync(32);
console.log(forge.util.bytesToHex(bytes));
// get some random bytes asynchronously
forge.random.getBytes(32, function(err, bytes) {
console.log(forge.util.bytesToHex(bytes));
});
// collect some entropy if you'd like
forge.random.collect(someRandomBytes);
jQuery().mousemove(function(e) {
forge.random.collectInt(e.clientX, 16);
forge.random.collectInt(e.clientY, 16);
});
// specify a seed file for use with the synchronous API if you'd like
forge.random.seedFileSync = function(needed) {
// get 'needed' number of random bytes from somewhere
return fetchedRandomBytes;
};
// specify a seed file for use with the asynchronous API if you'd like
forge.random.seedFile = function(needed, callback) {
// get the 'needed' number of random bytes from somewhere
callback(null, fetchedRandomBytes);
});
// register the main thread to send entropy or a Web Worker to receive
// entropy on demand from the main thread
forge.random.registerWorker(self);
// generate a new instance of a PRNG with no collected entropy
var myPrng = forge.random.createInstance();
Provides queuing and synchronizing tasks in a web application.
Examples
// TODO
Provides utility functions, including byte buffer support, base64, bytes to/from hex, zlib inflate/deflate, etc.
Examples
// encode/decode base64
var encoded = forge.util.encode64(str);
var str = forge.util.decode64(encoded);
// encode/decode UTF-8
var encoded = forge.util.encodeUtf8(str);
var str = forge.util.decodeUtf8(encoded);
// bytes to/from hex
var bytes = forge.util.hexToBytes(hex);
var hex = forge.util.bytesToHex(bytes);
// create an empty byte buffer
var buffer = forge.util.createBuffer();
// create a byte buffer from raw binary bytes
var buffer = forge.util.createBuffer(input, 'raw');
// create a byte buffer from utf8 bytes
var buffer = forge.util.createBuffer(input, 'utf8');
// get the length of the buffer in bytes
buffer.length();
// put bytes into the buffer
buffer.putBytes(bytes);
// put a 32-bit integer into the buffer
buffer.putInt32(10);
// buffer to hex
buffer.toHex();
// get a copy of the bytes in the buffer
bytes.bytes(/* count */);
// empty this buffer and get its contents
bytes.getBytes(/* count */);
// convert a forge buffer into a Node.js Buffer
// make sure you specify the encoding as 'binary'
var forgeBuffer = forge.util.createBuffer();
var nodeBuffer = Buffer.from(forgeBuffer.getBytes(), 'binary');
// convert a Node.js Buffer into a forge buffer
// make sure you specify the encoding as 'binary'
var nodeBuffer = Buffer.from('CAFE', 'hex');
var forgeBuffer = forge.util.createBuffer(nodeBuffer.toString('binary'));
Provides logging to a javascript console using various categories and levels of verbosity.
Examples
// TODO
The flash README provides details on rebuilding the optional Flash component used for networking. It also provides details on Policy Server support.
When using this code please keep the following in mind:
Financial support is welcome and helps contribute to further development: