https://github.com/peculiarventures/pkcs11js
A Node.js implementation of the PKCS#11 2.40 interface
https://github.com/peculiarventures/pkcs11js
cryptography hsms pkcs pkcs11 softhsm2
Last synced: about 1 month ago
JSON representation
A Node.js implementation of the PKCS#11 2.40 interface
- Host: GitHub
- URL: https://github.com/peculiarventures/pkcs11js
- Owner: PeculiarVentures
- License: mit
- Created: 2016-07-23T14:26:56.000Z (almost 9 years ago)
- Default Branch: master
- Last Pushed: 2024-02-01T00:59:37.000Z (over 1 year ago)
- Last Synced: 2024-04-14T09:58:18.437Z (about 1 year ago)
- Topics: cryptography, hsms, pkcs, pkcs11, softhsm2
- Language: C++
- Homepage:
- Size: 1.54 MB
- Stars: 103
- Watchers: 11
- Forks: 33
- Open Issues: 17
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# PKCS11js
[](https://raw.githubusercontent.com/PeculiarVentures/graphene/master/LICENSE)

[](https://coveralls.io/github/PeculiarVentures/pkcs11js?branch=master)
[](https://badge.fury.io/js/pkcs11js)[](https://nodei.co/npm/pkcs11js/)
PKCS11js is a package for direct interaction with the PKCS#11 API, the standard interface for interacting with hardware crypto devices such as Smart Cards and Hardware Security Modules (HSMs). It was developed to the PKCS#11 2.40 specification and has been tested with a variety of devices.
**Versioning Note:**
- Version 1.x was implemented using the `nan` module, which allowed the package to be built for older versions of Node.js.
- Starting from version 2.x, the module has been rewritten to use `napi`. As a result, the minimum required Node.js version is now v18.For most use cases, we recommend our package [Graphene](https://github.com/PeculiarVentures/graphene), which provides a simplistic Object Oriented interface for interacting with PKCS#11 devices.
This was developed to the PKCS#11 2.40 specification. It should be easy enough to extend it for any new versions at a later date.
It has been tested with :
- [SoftHSM2](https://www.opendnssec.org/softhsm/)
- [Thales NShield](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/)**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.
## Installation
```
$ npm install pkcs11js
```## Documentation
[https://peculiarventures.github.io/pkcs11js/](https://peculiarventures.github.io/pkcs11js/)
### Install SoftHSM2
- For OSX see the [instructions here](https://github.com/opendnssec/SoftHSMv2/blob/develop/OSX-NOTES.md)
- For linux [instructions here](https://github.com/opendnssec/SoftHSMv2/blob/develop/README.md)
- For Windows [instructions here](https://github.com/disig/SoftHSM2-for-Windows#softhsm2-installer-for-ms-windows)## Examples
### Example #1
```javascript
var pkcs11js = require("pkcs11js");var pkcs11 = new pkcs11js.PKCS11();
pkcs11.load("/usr/local/lib/softhsm/libsofthsm2.so");pkcs11.C_Initialize();
try {
// Getting info about PKCS11 Module
var module_info = pkcs11.C_GetInfo();// Getting list of slots
var slots = pkcs11.C_GetSlotList(true);
var slot = slots[0];// Getting info about slot
var slot_info = pkcs11.C_GetSlotInfo(slot);
// Getting info about token
var token_info = pkcs11.C_GetTokenInfo(slot);// Getting info about Mechanism
var mechs = pkcs11.C_GetMechanismList(slot);
var mech_info = pkcs11.C_GetMechanismInfo(slot, mechs[0]);var session = pkcs11.C_OpenSession(slot, pkcs11js.CKF_RW_SESSION | pkcs11js.CKF_SERIAL_SESSION);
// Getting info about Session
var info = pkcs11.C_GetSessionInfo(session);
pkcs11.C_Login(session, 1, "password");/**
* Your app code here
*/
pkcs11.C_Logout(session);
pkcs11.C_CloseSession(session);
}
catch(e){
console.error(e);
}
finally {
pkcs11.C_Finalize();
}
```### Example #2
Generating secret key using AES mechanism
```javascript
var template = [
{ type: pkcs11js.CKA_CLASS, value: pkcs11js.CKO_SECRET_KEY },
{ type: pkcs11js.CKA_TOKEN, value: false },
{ type: pkcs11js.CKA_LABEL, value: "My AES Key" },
{ type: pkcs11js.CKA_VALUE_LEN, value: 256 / 8 },
{ type: pkcs11js.CKA_ENCRYPT, value: true },
{ type: pkcs11js.CKA_DECRYPT, value: true },
];
var key = pkcs11.C_GenerateKey(session, { mechanism: pkcs11js.CKM_AES_KEY_GEN }, template);
```### Example #3
Generating key pair using RSA-PKCS1 mechanism
```javascript
var publicKeyTemplate = [
{ type: pkcs11js.CKA_CLASS, value: pkcs11js.CKO_PUBLIC_KEY },
{ type: pkcs11js.CKA_TOKEN, value: false },
{ type: pkcs11js.CKA_LABEL, value: "My RSA Public Key" },
{ type: pkcs11js.CKA_PUBLIC_EXPONENT, value: new Buffer([1, 0, 1]) },
{ type: pkcs11js.CKA_MODULUS_BITS, value: 2048 },
{ type: pkcs11js.CKA_VERIFY, value: true }
];
var privateKeyTemplate = [
{ type: pkcs11js.CKA_CLASS, value: pkcs11js.CKO_PRIVATE_KEY },
{ type: pkcs11js.CKA_TOKEN, value: false },
{ type: pkcs11js.CKA_LABEL, value: "My RSA Private Key" },
{ type: pkcs11js.CKA_SIGN, value: true },
];
var keys = pkcs11.C_GenerateKeyPair(session, { mechanism: pkcs11js.CKM_RSA_PKCS_KEY_PAIR_GEN }, publicKeyTemplate, privateKeyTemplate);
```### Example #4
Generating key pair using ECDSA mechanism
```javascript
var publicKeyTemplate = [
{ type: pkcs11js.CKA_CLASS, value: pkcs11js.CKO_PUBLIC_KEY },
{ type: pkcs11js.CKA_TOKEN, value: false },
{ type: pkcs11js.CKA_LABEL, value: "My EC Public Key" },
{ type: pkcs11js.CKA_EC_PARAMS, value: new Buffer("06082A8648CE3D030107", "hex") }, // secp256r1
];
var privateKeyTemplate = [
{ type: pkcs11js.CKA_CLASS, value: pkcs11js.CKO_PRIVATE_KEY },
{ type: pkcs11js.CKA_TOKEN, value: false },
{ type: pkcs11js.CKA_LABEL, value: "My EC Private Key" },
{ type: pkcs11js.CKA_DERIVE, value: true },
];
var keys = pkcs11.C_GenerateKeyPair(session, { mechanism: pkcs11js.CKM_EC_KEY_PAIR_GEN }, publicKeyTemplate, privateKeyTemplate);
```### Example #4
Working with Object
```javascript
var nObject = pkcs11.C_CreateObject(session, [
{ type: pkcs11js.CKA_CLASS, value: pkcs11js.CKO_DATA },
{ type: pkcs11js.CKA_TOKEN, value: false },
{ type: pkcs11js.CKA_PRIVATE, value: false },
{ type: pkcs11js.CKA_LABEL, value: "My custom data" },
]);// Updating label of Object
pkcs11.C_SetAttributeValue(session, nObject, [{ type: pkcs11js.CKA_LABEL, value: "My custom data!!!" }]);// Getting attribute value
var label = pkcs11.C_GetAttributeValue(session, nObject, [
{ type: pkcs11js.CKA_LABEL },
{ type: pkcs11js.CKA_TOKEN }
]);
console.log(label[0].value.toString()); // My custom data!!!
console.log(!!label[1].value[0]); // false// Copying Object
var cObject = pkcs11.C_CopyObject(session, nObject, [
{ type: pkcs11js.CKA_CLASS},
{ type: pkcs11js.CKA_TOKEN},
{ type: pkcs11js.CKA_PRIVATE},
{ type: pkcs11js.CKA_LABEL},
]);// Removing Object
pkcs11.C_DestroyObject(session, cObject);
```### Example #4
Searching objects
**NOTE:** If template is not set for C_FindObjectsInit, then C_FindObjects returns all objects from slot
```javascript
pkcs11.C_FindObjectsInit(session, [{ type: pkcs11js.CKA_CLASS, value: pkcs11js.CKO_DATA }]);
var hObject = pkcs11.C_FindObjects(session);
while (hObject) {
var attrs = pkcs11.C_GetAttributeValue(session, hObject, [
{ type: pkcs11js.CKA_CLASS },
{ type: pkcs11js.CKA_TOKEN },
{ type: pkcs11js.CKA_LABEL }
]);
// Output info for objects from token only
if (attrs[1].value[0]){
console.log(`Object #${hObject}: ${attrs[2].value.toString()}`);
}
hObject = pkcs11.C_FindObjects(session);
}
pkcs11.C_FindObjectsFinal(session);
```### Example #5
Generating random values
```javascript
var random = pkcs11.C_GenerateRandom(session, new Buffer(20));
console.log(random.toString("hex"));
```or
```javascript
var random = new Buffer(20);
pkcs11.C_GenerateRandom(session, random);
console.log(random.toString("hex"));
```### Example #6
Digest
```javascript
pkcs11.C_DigestInit(_session, { mechanism: pkcs11js.CKM_SHA256 });pkcs11.C_DigestUpdate(session, new Buffer("Incoming message 1"));
pkcs11.C_DigestUpdate(session, new Buffer("Incoming message N"));var digest = pkcs11.C_DigestFinal(_session, Buffer(256 / 8));
console.log(digest.toString("hex"));
```### Example #7
Signing data
```javascript
pkcs11.C_SignInit(session, { mechanism: pkcs11js.CKM_SHA256_RSA_PKCS }, keys.privateKey);pkcs11.C_SignUpdate(session, new Buffer("Incoming message 1"));
pkcs11.C_SignUpdate(session, new Buffer("Incoming message N"));var signature = pkcs11.C_SignFinal(session, Buffer(256));
```Verifying data
```javascript
pkcs11.C_VerifyInit(session, { mechanism: pkcs11js.CKM_SHA256_RSA_PKCS }, keys.publicKey);pkcs11.C_VerifyUpdate(session, new Buffer("Incoming message 1"));
pkcs11.C_VerifyUpdate(session, new Buffer("Incoming message N"));var verify = pkcs11.C_VerifyFinal(session, signature);
```### Example #8
Encrypting data with AES-CBC mechanism
```javascript
var cbc_param = pkcs11.C_GenerateRandom(new Buffer(16));pkcs11.C_EncryptInit(
session,
{
mechanism: pkcs11js.CKM_AES_CBC,
parameter: cbc_param
},
secretKey
);var enc = new Buffer(0);
enc = Buffer.concat([enc, pkcs11.C_EncryptUpdate(session, new Buffer("Incoming data 1"), new Buffer(16))]);
enc = Buffer.concat([enc, pkcs11.C_EncryptUpdate(session, new Buffer("Incoming data N"), new Buffer(16))]);
enc = Buffer.concat([enc, pkcs11.C_EncryptFinal(session, new Buffer(16))]);console.log(enc.toString("hex"));
```Decrypting data with AES-CBC mechanism
```javascript
pkcs11.C_DecryptInit(
session,
{
mechanism: pkcs11js.CKM_AES_CBC,
parameter: cbc_param
},
secretKey
);var dec = new Buffer(0);
dec = Buffer.concat([dec, pkcs11.C_DecryptUpdate(session, enc, new Buffer(32))]);
dec = Buffer.concat([dec, pkcs11.C_DecryptFinal(session, new Buffer(16))]);console.log(dec.toString());
```### Example #9
Deriving key with ECDH mechanism
```javascript
// Receive public data from EC public key
var attrs = pkcs11.C_GetAttributeValue(session, publicKeyEC, [{ type: pkcs11js.CKA_EC_POINT }])
var ec = attrs[0].value;var derivedKey = pkcs11.C_DeriveKey(
session,
{
mechanism: pkcs11js.CKM_ECDH1_DERIVE,
parameter: {
type: pkcs11js.CK_PARAMS_EC_DH,
kdf: 2,
publicData: ec
}
},
privateKeyEC,
[
{ type: pkcs11js.CKA_CLASS, value: pkcs11js.CKO_SECRET_KEY },
{ type: pkcs11js.CKA_TOKEN, value: false },
{ type: pkcs11js.CKA_KEY_TYPE, value: pkcs11js.CKK_AES },
{ type: pkcs11js.CKA_LABEL, value: "Derived AES key" },
{ type: pkcs11js.CKA_ENCRYPT, value: true },
{ type: pkcs11js.CKA_VALUE_LEN, value: 256 / 8 }
]
);
```
### Example #10Initializing NSS crypto library
Use `options` parameter for `C_Initialize` function.
__Type__
```ts
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;
}
/**
* Initializes the Cryptoki library
* @param options Initialization options
* Supports implementation of standard `CK_C_INITIALIZE_ARGS` and extended NSS format.
* - if `options` is null or empty, it calls native `C_Initialize` with `NULL`
* - if `options` doesn't have `libraryParameters`, it uses `CK_C_INITIALIZE_ARGS` structure
* - if `options` has `libraryParameters`, it uses extended NSS structure
*/
C_Initialize(options?: InitializationOptions): void;
```__Code__
```js
const mod = new pkcs11.PKCS11();
mod.load("/usr/local/opt/nss/lib/libsoftokn3.dylib");mod.C_Initialize({
libraryParameters: "configdir='' certPrefix='' keyPrefix='' secmod='' flags=readOnly,noCertDB,noModDB,forceOpen,optimizeSpace",
});// Your code here
mod.C_Finalize();
```[More](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Reference/FC_Initialize) info about NSS params for `C_Initialize`
### Example #11
Detect a slot event
```javascript
var pkcs11js = require("pkcs11js");
var pkcs11 = new pkcs11js.PKCS11();
// Need a compliant Cryptoki Version 2.01 or later
pkcs11.load("/usr/local/lib/softhsm/libsofthsm2.so");pkcs11.C_Initialize();
try {
const slotId = pkcs11.C_WaitForSlotEvent(pkcs11js.CKF_DONT_BLOCK);
if (slotId) {
console.log(`Slot ${slotId} has been inserted`);
} else {
console.log(`No slot event`);
}
} catch (e) {
console.error(e);
} finally {
pkcs11.C_Finalize();
}
```## Suitability
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.## Bug Reporting
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.## Related
- [PKCS #11 2.40 Specification](http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/pkcs11-curr-v2.40.html)
- [Many PKCS #11 Specifications](http://www.cryptsoft.com/pkcs11doc/)
- [Attacking and Fixing PKCS#11 Security Tokens](http://www.lsv.ens-cachan.fr/Publis/PAPERS/PDF/BCFS-ccs10.pdf)
- [PERL PKCS #11 binding](https://github.com/dotse/p5-crypt-pkcs11)
- [.NET PKCS #11 binding](https://github.com/jariq/Pkcs11Interop)
- [Ruby PKCS #11 binding](https://github.com/larskanis/pkcs11)
- [OCaml PKCS #11 binding](https://github.com/ANSSI-FR/caml-crush)
- [OCaml PKCS #11 CLI](https://github.com/ANSSI-FR/opkcs11-tool)
- [Go PKCS #11 binding](https://github.com/miekg/pkcs11)
- [PKCS #11 Admin](http://www.pkcs11admin.net)
- [Node.js Foreign Function Interface](https://github.com/node-ffi/node-ffi)
- [GOST PKCS#11 constants](https://github.com/romanovskiy-k/pkcs11/blob/master/rtpkcs11t.h)
- [PKCS#11 logging proxy module](https://github.com/jariq/pkcs11-logger)
- [PKCS#11 Proxy](https://github.com/iksaif/pkcs11-proxy)
- [PKCS#11 Tests](https://github.com/google/pkcs11test)
- [OpenCryptoKi](http://sourceforge.net/projects/opencryptoki/)
- [SoftHSM](https://www.opendnssec.org/softhsm/)
- [SofHSM2 for Windows](https://github.com/disig/SoftHSM2-for-Windows/)
- [node-pcsc](https://github.com/santigimeno/node-pcsclite)
- [PKCS#11 URIs](https://tools.ietf.org/html/rfc7512)
- [Key Length Recommendations](http://www.keylength.com/en/compare/)