Security News
New Python Packaging Proposal Aims to Solve Phantom Dependency Problem with SBOMs
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.
graphene-pk11
Advanced tools
A simple layer for interacting with PKCS #11 / PKCS11 / CryptoKI for Node in TypeScript
A simple layer for interacting with PKCS #11 / PKCS11 / CryptoKI for Node in TypeScript
PKCS #11 (also known as CryptoKI or PKCS11) is the standard interface for interacting with hardware crypto devices such as Smart Cards and Hardware Security Modules (HSMs). It wraps the library closely, but uses attempts to look like 'node.crypto' where it makes sense.
It has been tested with :
NOTE: For testing purposes it may be easier to work with SoftHSM2 which is a software implementation of PKCS#11 based on OpenSSL or Botan.
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
Install the package
$ npm install graphene-pk11 --global
Create config file
// config.json
{
"lib": "/usr/local/lib/softhsm/libsofthsm2.so",
"libName": "SoftHSMv2.0",
"slot": 0,
"pin": "password"
}
Start console application
$ graphene
Load module from config file
$ > module init -p config.json
Get list of objects
$ > object list -s 0
Install the package
$ npm install graphene-pk11 --save
Install TypeScript definition using TSD package manager
$ tsd install graphene-pk11 --save
Load module
// file.js
var graphene = require("graphene-pk11");
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();
####Output
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();
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();
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: new Buffer([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: new Buffer([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: new Buffer([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();
Use npm command to publish graphene-pk11 module
> npm run-script pub
At this time this solution should be considered suitable for research and experimentation, further code and security review is needed before utilization in a production application.
Please report bugs either as pull requests or as issues in the issue tracker. Graphene has a full disclosure vulnerability policy. Please do NOT attempt to report any security vulnerability in this code privately to anybody.
FAQs
A simple layer for interacting with PKCS #11 / PKCS11 / CryptoKI for Node in TypeScript
We found that graphene-pk11 demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.
Security News
Socket CEO Feross Aboukhadijeh discusses open source security challenges, including zero-day attacks and supply chain risks, on the Cyber Security Council podcast.
Security News
Research
Socket researchers uncover how threat actors weaponize Out-of-Band Application Security Testing (OAST) techniques across the npm, PyPI, and RubyGems ecosystems to exfiltrate sensitive data.