• Stars
    star
    162
  • Rank 232,284 (Top 5 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created about 9 years ago
  • Updated 6 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A simple layer for interacting with PKCS #11 / PKCS11 / CryptoKI for Node in TypeScript. (Keywords: Javascript, PKCS#11, Crypto, Smart Card, HSM)

Graphene

license test Coverage Status npm version

NPM

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 :

We have also created a basic CLI for interacting with PKCS#11 devices based on this library we call graphene-cli.

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();

Installation

$ npm install graphene-pk11

Documentation

https://peculiarventures.github.io/graphene/

Using the Package

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");

Install SoftHSM2

Examples

Listing capabilities

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              -/-/-/+/+/+/+

Hashing

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();

Generating keys

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();

Exporting public key

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=="
  }

*/

Signing / Verifying

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();

Encrypting / Decrypting

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();

Derive key

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();

Change User's 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();

Adding x509 certificate

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();

Initializing NSS crypto library

Use options parameter for Module::initialize method.

Type

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;
}

Code

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();

Developing

Use npm command to publish graphene-pk11 module

> npm run pub

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.

TODO

  • Add tests to the library
  • Add additional capabilities to CLI (device initialization, file signing, file encryption, etc)

Related

More Repositories

1

PKI.js

PKI.js is a pure JavaScript library implementing the formats that are used in PKI applications (signing, encryption, certificate requests, OCSP and TSP requests/responses). It is built on WebCrypto (Web Cryptography API) and requires no plug-ins.
TypeScript
1,300
star
2

ASN1.js

ASN1js is a pure JavaScript library implementing a full ASN.1 BER decoder and encoder.
TypeScript
267
star
3

webcrypto

A WebCrypto Polyfill for NodeJS
TypeScript
183
star
4

GammaCV

GammaCV is a WebGL accelerated Computer Vision library for browser
JavaScript
175
star
5

webcrypto-liner

webcrypto-liner is a polyfill that let's down-level User Agents (like IE/Edge) use libraries that depend on WebCrypto. (Keywords: Javascript, WebCrypto, Shim, Polyfill)
TypeScript
149
star
6

js-zxing-pdf417

Javascript port of the PDF417 detector and decoder from http://github.com/zxing/zxing (Keywords: Barcode, PDF 417, Javascript)
JavaScript
142
star
7

xadesjs

A pure Typescript/Javascript implementation of XAdES based on XMLDSIGjs. (Keywords: WebCrypto, XMLDSIG, XADES, eIDAS, Trust List, X.509, CRL, OCSP)
TypeScript
140
star
8

node-webcrypto-ossl

A WebCrypto Polyfill for Node in TypeScript built on OpenSSL.
C++
128
star
9

fortify

Fortify enables web applications to use smart cards, local certificate stores and do certificate enrollment. This is the desktop application repository.
TypeScript
114
star
10

2key-ratchet

2key-ratchet is an implementation of a Double Ratchet protocol and X3DH in TypeScript utilizing WebCrypto.
TypeScript
109
star
11

pkcs11js

A Node.js implementation of the PKCS#11 2.40 interface
C++
107
star
12

x509

@peculiar/x509 is an easy to use TypeScript/Javascript library based on @peculiar/asn1-schema that makes generating X.509 Certificates and Certificate Requests as well as validating certificate chains easy
TypeScript
81
star
13

pv-certificates-viewer

Web components for viewing lists of certificates and certificates
TypeScript
61
star
14

xmldsigjs

XMLDSIGjs provides an implementation of XMLDSIG in Typescript/Javascript based on WebCrypto
TypeScript
45
star
15

node-webcrypto-p11

A WebCrypto Polyfill for Node in typescript built on PKCS#11.
TypeScript
43
star
16

asn1-schema

asn1-schema is a collection of TypeScript schemas that make working with common ASN.1 objects easy
TypeScript
33
star
17

tl-create

tl-create is a cross-platform command line tool to create a X.509 trust list from various trust stores. (Keywords: CABFORUM, eIDAS, WebPKI)
HTML
33
star
18

pvpkcs11

pvpkcs11 consists of a input validation library and a set of PKCS#11 implementations that wrap operating system and browser cryptographic implementations.
C++
32
star
19

csrhelp

csrhelp.peculiarventures.com - A site that helps users generate SSL certificate requests (Keywords: WebCrypto, PKIjs, PKCS#10, CSR)
JavaScript
27
star
20

webcrypto-core

A input validation layer for WebCrypto polyfills.
TypeScript
27
star
21

tsprotobuf

tsprotobuf is a helper library that contains functions that make working with ProtoBuf easier in Typescript.
TypeScript
21
star
22

xml-core

xml-core is a set of classes that make it easier to work with XML within the browser and node.
TypeScript
19
star
23

CAdES.js

CAdESjs is an implementation of CAdES (CMS Advanced Electronic Signatures)in pure Javascript.
JavaScript
18
star
24

webcrypto-local

webcrypto-local is a cross platform service that provides access to PKCS#11 implementations over a protocol we call webcrypto-socket.
TypeScript
18
star
25

fortify-tools

Fortify enables web applications to use smart cards, local certificate stores and do certificate enrollment. This is the "Tool" application used in the Fortify desktop application.
JavaScript
15
star
26

ByteStream.js

ByteStream.js is a set of classes manipulating bytes and bits with optimized for speed perfomance
TypeScript
13
star
27

acme-ts

Provides client and server implementations of ACME (RFC 8555) in TypeScript. It enables you to build solutions that provide complete and robust certificate lifecycle management.
TypeScript
12
star
28

fortify-examples

Fortify enables web applications to use smart cards, local certificate stores and do certificate enrollment. This is a set of examples of how to use Fortify in your own applications.
JavaScript
8
star
29

pvutils

pvutils is a set of common utility functions used in various Peculiar Ventures Javascript based projects.
TypeScript
7
star
30

acme-cs

Provides client and server implementations of ACME (RFC 8555) in C-Sharp. It enables you to build solutions that provide complete and robust certificate lifecycle management.
C#
6
star
31

graphene-cli

The graphene-cli is a cross-platform command line tool for working with PKCS#11 devices
TypeScript
5
star
32

webcrypto-docs

5
star
33

pv-webcrypto-tests

A basic test suite for WebCrypto.
JavaScript
5
star
34

PVCertViewer

Example certificate viewer based on PKIjs
JavaScript
5
star
35

webcrypto.dev-examples

Peculiar Ventures' webcrypto.dev is a collection of cryptography and X.509 certificate libraries, making it easier for developers to integrate these technologies into their projects.
TypeScript
4
star
36

json-schema

This package uses ES2015 decorators to simplify JSON schema creation and use
TypeScript
3
star
37

pvtsutils

pvtsutils is a set of common utility functions used in various Peculiar Ventures TypeScript based projects.
TypeScript
3
star
38

validatewallet.com

validatewallet.com website
HTML
2
star
39

Font.js

FontJS (Font.js) is a packages for TrueType font parsing and manipulation
TypeScript
2
star
40

peculiar-react-components

JavaScript
2
star
41

validatewallet

HTML
1
star
42

ExamplePDFs

1
star
43

pkcs11test

Simple CLI application for PKCS#11 testing based on WebCrypto library
TypeScript
1
star
44

peculiar-ui

TypeScript
1
star