PeculiarVentures/graphene
GitHub: PeculiarVentures/graphene
面向 Node.js 的 PKCS#11 接口层,让 TypeScript 应用能够与 HSM 硬件安全模块和智能卡进行交互,实现硬件级加密运算。
Stars: 181 | Forks: 33
# Graphene
[](https://raw.githubusercontent.com/PeculiarVentures/graphene/master/LICENSE)

[](https://coveralls.io/github/PeculiarVentures/graphene?branch=master)
[](https://badge.fury.io/js/graphene-pk11)
[](https://nodei.co/npm/graphene-pk11/)
一个简单的层,用于在 TypeScript 中与 Node 的 PKCS #11 / PKCS11 / CryptoKI 进行交互
PKCS #11(也称为 CryptoKI 或 PKCS11)是与硬件加密设备(如智能卡和硬件安全模块 (HSM))进行交互的标准接口。它紧密封装了库,但在合理的地方尝试看起来像 'node.crypto'。
它已在以下设备上测试过:
- [SoftHSM2](https://www.opendnssec.org/softhsm/)
- [Thales nShield Solo+](https://www.thales-esecurity.com/products-and-services/products-and-services/hardware-security-modules/general-purpose-hsms/nshield-solo)
- [Safenet Luna HSMs](http://www.safenet-inc.com/)
- [RuToken](http://www.rutoken.ru/)
我们还基于此库创建了一个基本的 [CLI](https://github.com/PeculiarVentures/graphene-cli),用于与 PKCS#11 设备交互,我们称之为 [graphene-cli](https://github.com/PeculiarVentures/graphene-cli)。
**注意:** 出于测试目的,使用 SoftHSM2 可能更容易,它是基于 OpenSSL 或 Botan 的 PKCS#11 软件实现。
```
var graphene = require("graphene-pk11");
var Module = graphene.Module;
var mod = Module.load("/usr/local/lib/softhsm/libsofthsm2.so", "SoftHSM");
mod.initialize();
var session = mod.getSlots(0).open();
session.login("password");
// Get a number of private key objects on token
console.log(session.find({class: graphene.ObjectClass.PRIVATE_KEY}).length);
session.logout();
mod.finalize();
```
## 安装
```
$ npm install graphene-pk11
```
## 文档
[https://peculiarventures.github.io/graphene/](https://peculiarventures.github.io/graphene/)
## 使用该包
安装该包
```
$ npm install graphene-pk11 --save
```
使用 [TSD](https://www.npmjs.com/package/tsd) 包管理器安装 TypeScript 定义
```
$ tsd install graphene-pk11 --save
```
加载模块
```
// file.js
var graphene = require("graphene-pk11");
```
### 安装 SoftHSM2
- 对于 OSX,请参阅[此处的说明](https://github.com/opendnssec/SoftHSMv2/blob/develop/OSX-NOTES.md)
- 对于 linux,请参阅[此处的说明](https://github.com/opendnssec/SoftHSMv2/blob/develop/README.md)
## 示例
### 列出功能
```
var graphene = require("graphene-pk11");
var Module = graphene.Module;
var lib = "/usr/local/lib/softhsm/libsofthsm2.so";
var mod = Module.load(lib, "SoftHSM");
mod.initialize();
// get slots
var slots = mod.getSlots(true);
if (slots.length > 0) {
for (var i = 0; i < slots.length; i++) {
var slot = slots.items(i);
console.log("Slot #" + slot.handle);
console.log("\tDescription:", slot.slotDescription);
console.log("\tSerial:", slot.getToken().serialNumber);
console.log("\tPassword(min/max): %d/%d", slot.getToken().minPinLen, slot.getToken().maxPinLen);
console.log("\tIs hardware:", !!(slot.flags & graphene.SlotFlag.HW_SLOT));
console.log("\tIs removable:", !!(slot.flags & graphene.SlotFlag.REMOVABLE_DEVICE));
console.log("\tIs initialized:", !!(slot.flags & graphene.SlotFlag.TOKEN_PRESENT));
console.log("\n\nMechanisms:");
console.log("Name h/s/v/e/d/w/u");
console.log("========================================");
function b(v) {
return v ? "+" : "-";
}
function s(v) {
v = v.toString();
for (var i_1 = v.length; i_1 < 27; i_1++) {
v += " ";
}
return v;
}
var mechs = slot.getMechanisms();
for (var j = 0; j < mechs.length; j++) {
var mech = mechs.items(j);
console.log(s(mech.name) +
b(mech.flags & graphene.MechanismFlag.DIGEST) + "/" +
b(mech.flags & graphene.MechanismFlag.SIGN) + "/" +
b(mech.flags & graphene.MechanismFlag.VERIFY) + "/" +
b(mech.flags & graphene.MechanismFlag.ENCRYPT) + "/" +
b(mech.flags & graphene.MechanismFlag.DECRYPT) + "/" +
b(mech.flags & graphene.MechanismFlag.WRAP) + "/" +
b(mech.flags & graphene.MechanismFlag.UNWRAP));
}
}
}
mod.finalize();
```
####输出
```
Slot #0
Description: SoftHSM slot 0
Serial: f89e34b310e83df2
Password(min/max): 4/255
Is hardware: false
Is removable: false
Is initialized: true
Mechanisms:
Name h/s/v/e/d/w/u
========================================
MD5 +/-/-/-/-/-/-
SHA_1 +/-/-/-/-/-/-
SHA224 +/-/-/-/-/-/-
SHA256 +/-/-/-/-/-/-
SHA384 +/-/-/-/-/-/-
SHA512 +/-/-/-/-/-/-
MD5_HMAC -/+/+/-/-/-/-
SHA_1_HMAC -/+/+/-/-/-/-
SHA224_HMAC -/+/+/-/-/-/-
SHA256_HMAC -/+/+/-/-/-/-
SHA384_HMAC -/+/+/-/-/-/-
SHA512_HMAC -/+/+/-/-/-/-
RSA_PKCS_KEY_PAIR_GEN -/-/-/-/-/-/-
RSA_PKCS -/+/+/+/+/+/+
RSA_X_509 -/+/+/+/+/-/-
MD5_RSA_PKCS -/+/+/-/-/-/-
SHA1_RSA_PKCS -/+/+/-/-/-/-
RSA_PKCS_OAEP -/-/-/+/+/+/+
```
### 哈希
```
var graphene = require("graphene-pk11");
var Module = graphene.Module;
var lib = "/usr/local/lib/softhsm/libsofthsm2.so";
var mod = Module.load(lib, "SoftHSM");
mod.initialize();
var slot = mod.getSlots(0);
if (slot.flags & graphene.SlotFlag.TOKEN_PRESENT) {
var session = slot.open();
var digest = session.createDigest("sha1");
digest.update("simple text 1");
digest.update("simple text 2");
var hash = digest.final();
console.log("Hash SHA1:", hash.toString("hex")); // Hash SHA1: e1dc1e52e9779cd69679b3e0af87d2e288190d34
session.close();
}
else {
console.error("Slot is not initialized");
}
mod.finalize();
```
### 生成密钥
#### AES
```
var graphene = require("graphene-pk11");
var Module = graphene.Module;
var lib = "/usr/local/lib/softhsm/libsofthsm2.so";
var mod = Module.load(lib, "SoftHSM");
mod.initialize();
var slot = mod.getSlots(0);
if (slot.flags & graphene.SlotFlag.TOKEN_PRESENT) {
var session = slot.open();
session.login("12345");
var k = session.generateKey(graphene.KeyGenMechanism.AES, {
"class": graphene.ObjectClass.SECRET_KEY,
"token": false,
"valueLen": 256 / 8,
"keyType": graphene.KeyType.AES,
"label": "My AES secret key",
"private": true
});
console.log("Key.handle:", k.handle); // Key.handle: 2
console.log("Key.type:", graphene.KeyType[k.type]); // Key.type: AES
session.logout();
session.close();
}
else {
console.error("Slot is not initialized");
}
mod.finalize();
```
#### ECC
```
var graphene = require("graphene-pk11");
var Module = graphene.Module;
var lib = "/usr/local/lib/softhsm/libsofthsm2.so";
var mod = Module.load(lib, "SoftHSM");
mod.initialize();
var slot = mod.getSlots(0);
if (slot.flags & graphene.SlotFlag.TOKEN_PRESENT) {
var session = slot.open();
session.login("12345");
// generate ECDSA key pair
var keys = session.generateKeyPair(graphene.KeyGenMechanism.ECDSA, {
keyType: graphene.KeyType.ECDSA,
token: false,
verify: true,
paramsECDSA: graphene.NamedCurve.getByName("secp192r1").value
}, {
keyType: graphene.KeyType.ECDSA,
token: false,
sign: true
});
console.log("Key type:", graphene.KeyType[keys.privateKey.type]); // Key type: ECDSA
console.log("Object's class:", graphene.ObjectClass[keys.privateKey.class]); // Object's class: PRIVATE_KEY
session.logout();
session.close();
}
else {
console.error("Slot is not initialized");
}
mod.finalize();
```
### 导出公钥
```
var graphene = require("graphene-pk11");
var Module = graphene.Module;
var lib = "/usr/local/lib/softhsm/libsofthsm2.so";
var mod = Module.load(lib, "SoftHSM");
mod.initialize();
var slot = mod.getSlots(0);
if (slot.flags & graphene.SlotFlag.TOKEN_PRESENT) {
var session = slot.open();
session.login("12345");
// generate RSA key pair
var keys = session.generateKeyPair(graphene.KeyGenMechanism.RSA, {
keyType: graphene.KeyType.RSA,
modulusBits: 1024,
publicExponent: Buffer.from([3]),
token: false,
verify: true,
encrypt: true,
wrap: true
}, {
keyType: graphene.KeyType.RSA,
token: false,
sign: true,
decrypt: true,
unwrap: true
});
// get public key attributes
var pubKey = keys.publicKey.getAttribute({
modulus: null,
publicExponent: null
});
// convert values to base64
pubKey.modulus = pubKey.modulus.toString("base64");
pubKey.publicExponent = pubKey.publicExponent.toString("base64");
console.log(JSON.stringify(pubKey, null, 4));
session.logout();
session.close();
}
else {
console.error("Slot is not initialized");
}
mod.finalize();
/*
Result
------------------
{
"modulus": "21HTpGsKn3lQh4fqhYkZ/NprzKZqCnUIs0Ekbg8Y0M0Er4yJ4tKVFLlaxUkym6nRBQuS2tzwSQcvuKVUNeK3k6AiPitlQs5CRc8csqL6BYMU+rme3L0w/d+1OryH/pMrDGOmkWXTrzBWoRgulXHX92jK6CcXKBeS/yUSgCLP/MM=",
"publicExponent": "Aw=="
}
*/
```
### 签名 / 验证
```
var graphene = require("graphene-pk11");
var Module = graphene.Module;
var lib = "/usr/local/lib/softhsm/libsofthsm2.so";
var mod = Module.load(lib, "SoftHSM");
mod.initialize();
var slot = mod.getSlots(0);
if (slot.flags & graphene.SlotFlag.TOKEN_PRESENT) {
var session = slot.open();
session.login("12345");
// generate RSA key pair
var keys = session.generateKeyPair(graphene.KeyGenMechanism.RSA, {
keyType: graphene.KeyType.RSA,
modulusBits: 1024,
publicExponent: Buffer.from([3]),
token: false,
verify: true,
encrypt: true,
wrap: true
}, {
keyType: graphene.KeyType.RSA,
token: false,
sign: true,
decrypt: true,
unwrap: true
});
// sign content
var sign = session.createSign("SHA1_RSA_PKCS", keys.privateKey);
sign.update("simple text 1");
sign.update("simple text 2");
var signature = sign.final();
console.log("Signature RSA-SHA1:", signature.toString("hex")); // Signature RSA-SHA1: 6102a66dc0d97fadb5...
// verify content
var verify = session.createVerify("SHA1_RSA_PKCS", keys.publicKey);
verify.update("simple text 1");
verify.update("simple text 2");
var verify_result = verify.final(signature);
console.log("Signature RSA-SHA1 verify:", verify_result); // Signature RSA-SHA1 verify: true
session.logout();
session.close();
}
else {
console.error("Slot is not initialized");
}
mod.finalize();
```
### 加密 / 解密
```
var graphene = require("graphene-pk11");
var Module = graphene.Module;
var lib = "/usr/local/lib/softhsm/libsofthsm2.so";
var mod = Module.load(lib, "SoftHSM");
mod.initialize();
var slot = mod.getSlots(0);
if (slot.flags & graphene.SlotFlag.TOKEN_PRESENT) {
var session = slot.open();
session.login("12345");
// generate AES key
var key = session.generateKey(graphene.KeyGenMechanism.AES, {
"class": graphene.ObjectClass.SECRET_KEY,
"token": false,
"valueLen": 256 / 8,
"keyType": graphene.KeyType.AES,
"label": "My AES secret key",
"encrypt": true,
"decrypt": true
});
// enc algorithm
var alg = {
name: "AES_CBC_PAD",
params: Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]) // IV
};
var MESSAGE = "Encrypted message";
// encrypting
var cipher = session.createCipher(alg, key);
var enc = cipher.update(MESSAGE);
enc = Buffer.concat([enc, cipher.final()]);
console.log("Enc:", enc.toString("hex")); // Enc: eb21e15b896f728a4...
// decrypting
var decipher = session.createDecipher(alg, key);
var dec = decipher.update(enc);
var msg = Buffer.concat([dec, decipher.final()]).toString();
console.log("Message:", msg.toString()); // Message: Encrypted message
session.logout();
session.close();
}
else {
console.error("Slot is not initialized");
}
mod.finalize();
```
### 派生密钥
```
var graphene = require("graphene-pk11");
var Module = graphene.Module;
var lib = "/usr/local/lib/softhsm/libsofthsm2.so";
var mod = Module.load(lib, "SoftHSM");
mod.initialize();
var slot = mod.getSlots(0);
if (slot.flags & graphene.SlotFlag.TOKEN_PRESENT) {
var session = slot.open();
session.login("12345");
// generate EC key
var keys = session.generateKeyPair(graphene.KeyGenMechanism.ECDSA, {
keyType: graphene.KeyType.ECDSA,
token: false,
derive: true,
paramsECDSA: graphene.NamedCurve.getByName("secp192r1").value
}, {
keyType: graphene.KeyType.ECDSA,
token: false,
derive: true
});
// derive algorithm
var alg = {
name: "ECDH1_DERIVE",
params: new graphene.EcdhParams(
graphene.EcKdf.SHA1,
null,
keys.publicKey.getAttribute({pointEC: null}).pointEC)
};
// Template for derived key
var template = {
"class": graphene.ObjectClass.SECRET_KEY,
"token": false,
"keyType": graphene.KeyType.AES,
"valueLen": 256 / 8,
"encrypt": true,
"decrypt": true
}
// Key derivation
var dKey = session.deriveKey(alg, keys.privateKey, template)
console.log("Derived key handle:", dKey.handle);
session.logout();
session.close();
}
else {
console.error("Slot is not initialized");
}
mod.finalize();
```
### 更改用户 PIN
```
var graphene = require("graphene-pk11");
var Module = graphene.Module;
var lib = "/usr/local/lib/softhsm/libsofthsm2.so";
var mod = Module.load(lib, "SoftHSM");
mod.initialize();
try {
var slot = mod.getSlots(0);
if (slot.flags & graphene.SlotFlag.TOKEN_PRESENT) {
var session = slot.open();
session.login("12345", graphene.UserType.USER);
session.setPin("12345", "new pin");
session.logout();
session.close();
console.log("User's PIN was changed successfully");
}
}
catch(e) {
console.error(e);
}
mod.finalize();
```
### 添加 x509 证书
```
const graphene = require("graphene-pk11");
const mod = graphene.Module.load("/usr/local/lib/softhsm/libsofthsm2.so", "SoftHSM");
mod.initialize();
try {
const slot = mod.getSlots(0);
const session = slot.open(2 | 4)
session.login("password");
const template = {
class: graphene.ObjectClass.CERTIFICATE,
certType: graphene.CertificateType.X_509,
private: false,
token: false,
id: Buffer.from([1, 2, 3, 4, 5]), // Should be the same as Private/Public key has
label: "My certificate",
subject: Buffer.from("3034310B300906035504...", "hex"),
value: Buffer.from("308203A830820290A003...", "hex"),
};
const objCert = session.create(template).toType();
console.log("Certificate: created\n");
console.log("Certificate info:\n===========================");
console.log("Handle:", objCert.handle.toString("hex"));
console.log("ID:", objCert.id.toString("hex"));
console.log("Label:", objCert.label);
console.log("category:", graphene.CertificateCategory[objCert.category]);
console.log("Subject:", objCert.subject.toString("hex"));
console.log("Value:", objCert.value.toString("hex"));
} catch (err) {
console.error(err);
}
mod.finalize();
```
### 初始化 NSS 加密库
为 `Module::initialize` 方法使用 `options` 参数。
__类型__
```
interface InitializationOptions {
/**
* NSS library parameters
*/
libraryParameters?: string;
/**
* bit flags specifying options for `C_Initialize`
* - CKF_LIBRARY_CANT_CREATE_OS_THREADS. True if application threads which are executing calls to the library
* may not use native operating system calls to spawn new threads; false if they may
* - CKF_OS_LOCKING_OK. True if the library can use the native operation system threading model for locking;
* false otherwise
*/
flags?: number;
}
```
__代码__
```
const mod = Module.load("/usr/local/opt/nss/lib/libsoftokn3.dylib", "NSS");
mod.initialize({
libraryParameters: "configdir='' certPrefix='' keyPrefix='' secmod='' flags=readOnly,noCertDB,noModDB,forceOpen,optimizeSpace",
});
// Your code here
mod.finalize();
```
## 开发
使用 npm 命令发布 graphene-pk11 模块
```
> npm run pub
```
## 适用性
目前,此解决方案应被认为适合用于研究和实验,在生产应用程序中使用之前,需要进一步的代码和安全审查。
## Bug 报告
请以 pull requests 或 issue tracker 中的 issues 形式报告 bug。Graphene 拥有完全披露的漏洞政策。请勿尝试私下向任何人报告此代码中的任何安全漏洞。
## 待办事项
* 为库添加测试
* 向 CLI 添加额外功能(设备初始化、文件签名、文件加密等)
## 相关
- [PKCS #11 2.40 规范](http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/pkcs11-curr-v2.40.html)
- [许多 PKCS #11 规范](http://www.cryptsoft.com/pkcs11doc/)
- [PERL PKCS #11 绑定](https://github.com/dotse/p5-crypt-pkcs11)
- [.NET PKCS #11 绑定](https://github.com/jariq/Pkcs11Interop)
- [Ruby PKCS #11 绑定](https://github.com/larskanis/pkcs11)
- [OCaml PKCS #11 绑定](https://github.com/ANSSI-FR/caml-crush)
- [OCaml PKCS #11 CLI](https://github.com/ANSSI-FR/opkcs11-tool)
- [Go PKCS #11 绑定](https://github.com/miekg/pkcs11)
- [PKCS #11 管理工具](http://www.pkcs11admin.net)
- [Node.js 外部函数接口](https://github.com/node-ffi/node-ffi)
- [GOST PKCS#11 常量](https://github.com/romanovskiy-k/pkcs11/blob/master/rtpkcs11t.h)
- [PKCS#11 日志代理模块](https://github.com/jariq/pkcs11-logger)
- [PKCS#11 代理](https://github.com/iksaif/pkcs11-proxy)
- [PKCS#11 测试](https://github.com/google/pkcs11test)
- [OpenCryptoKi](http://sourceforge.net/projects/opencryptoki/)
- [SoftHSM](https://www.opendnssec.org/softhsm/)
- [Windows 版 SoftHSM2](https://github.com/disig/SoftHSM2-for-Windows/)
- [node-pcsc](https://github.com/santigimeno/node-pcsclite)
- [PKCS#11 URI](https://tools.ietf.org/html/rfc7512)
- [密钥长度建议](http://www.keylength.com/en/compare/)
标签:CMS安全, CryptoKI, CVE, GNU通用公共许可证, HSM, HTTP工具, JavaScript, MITM代理, Node.js, OpenSSL, PKCS#11, RuToken, SafeNet Luna, SamuraiWTF, SIEM, SoftHSM, Thales nShield, TypeScript, 加密, 后端开发, 安全插件, 密码学, 底层库, 手动系统调用, 数字签名, 智能卡, 漏洞扫描器, 硬件安全模块, 网络安全, 隐私保护