PKCS11 Objects

Sample code uses ES6 language features such as arrow functions and promises. For compatibility with IE11, code written with these features must be either transpiled using tools like Babel or refactored accordingly using callbacks.

Introduction

The PKCS #11 standard defines a platform-independent API to cryptographic tokens, such as hardware security modules (HSM), smart cards, and names the API itself "Cryptoki" (from "cryptographic token interface" and pronounced as "crypto-key" - but "PKCS #11" is often used to refer to the API as well as the standard that defines it). The API defines most commonly used cryptographic object types (RSAX.509 keys, DES/Triple DES Certificates/keys, etc.) and all the functions needed to use, create/generate, modify and delete those objects. This container relies on a PKCS#11 a library which handles the communication with the token/card. This can be a vendor specific library or an opensource one, please select the correct one depending on the type of token/card you are using.

Interface Summary

The Abstract PKCS #11 smartcard interface is summarised in the following snippet:

export interface AbstractPkcs11 {
    certificates(slotId: string, parseCerts?: boolean, callback?: (error: T1CLibException, data: Pkcs11ObjectCertificatesResponse) => void): Promise<Pkcs11ObjectCertificatesResponse>;
    signData(data: Pkcs11SignData, callback?: (error: T1CLibException, data: Pkcs11ObjectSignResponse) => void): Promise<Pkcs11ObjectSignResponse>;
    slots(callback?: (error: T1CLibException, data: Pkcs11ObjectSlotsResponse) => void): Promise<Pkcs11ObjectSlotsResponse>;
    slotsWithTokenPresent(callback?: (error: T1CLibException, data: Pkcs11ObjectSlotsResponse) => void): Promise<Pkcs11ObjectSlotsResponse>;
    token(slotId: string, callback?: (error: T1CLibException, data: Pkcs11ObjectTokenResponse) => void): Promise<Pkcs11ObjectTokenResponse>;
}

Pkcs11 object models

export class Pkcs11ObjectInfoResponse extends DataObjectResponse {
    constructor(public data: Pkcs11Info, public success: boolean) {
        super(data, success);
    }
}

export class Pkcs11ObjectSign {
    constructor(public data: string) {
    }
}

export class Pkcs11ObjectSignResponse {
    constructor(public data: Pkcs11ObjectSign, public success: boolean) {
    }
}

export class Pkcs11ObjectSlots {
    constructor(public slots: Pkcs11Slot[]) {
    }
}

export class Pkcs11ObjectSlotsResponse {
    constructor(public data: Pkcs11ObjectSlots, public success: boolean) {
    }
}


export class Pkcs11ObjectCertificates {
    constructor(public certificates: Pkcs11ObjectCertificate[]) {}
}


export class Pkcs11ObjectCertificate {
    constructor(public id: string, public certificate: string, public parsed?: Certificate) {}
}

export class Pkcs11ObjectCertificatesResponse {
    constructor(public data: Pkcs11ObjectCertificates, public success: boolean) {
    }
}

export class Pkcs11SignData {
    constructor(public slotId: string,
                public certificateId: string,
                public algorithm: string,
                public data: string,
                public pin?: string,
                public osDialog?: boolean) {}
}

export class Pkcs11ObjectTokenResponse {
    constructor(public data: Pkcs11TokenInfo, public success: boolean) {
    }
}

export class Pkcs11SetConfigResponse {
    constructor(public data: string, public success: boolean) {
    }
}

export class Pkcs11Slots {
    constructor(public slots: Pkcs11Slot[]) {
    }
}

export class Pkcs11Slot {
    constructor(public slot: number,
                public description: string) {
    }
}

export class Pkcs11TokenInfo {
    constructor(public slot: string,
                public label: string,
                public manufacturerId: string,
                public model: string,
                public serialNumber: string,
                public flags: string,
                public ulMaxSessionCount: number,
                public ulSessionCount: number,
                public ulMaxRwSessionCount: number,
                public ulMaxPinLen: number,
                public ulMinPinLen: number,
                public ulTotalPublicMemory: number,
                public ulFreePublicMemory: number,
                public ulTotalPrivateMemory: number,
                public ulFreePrivateMemory: number,
                public hardwareVersion: string,
                public firmwareVersion: string) {
    }
}

Get the PKCS #11 container object

For more information on how to configure the JS client library see Configuration.

const winLocation = "C:\\Windows\\System32\\eTPKCS11.dll"
const macLocation = "/usr/local/lib/libeTPkcs11.dylib";

T1CSdk.T1CClient.initialize(config).then(res => {
  client = res;
  console.log("Client config: ", client.localConfig)
  core = client.core();
  
  // Depending on the OS select the appropriate location of the library
  const pkcs11 = client.pkcs11(winLocation);
  
}, err => {});

Depending on the OS you need to provide a valid location to the desired PKCS11 library to be used.

Call a function for the PKCS #11 container:

const slotId = 0;

pkcs11.certificates(slotId, true).then(res => {
    console.log(JSON.stringify(data, null, '  '));
}, err => {
    console.log("Error:",JSON.stringify(err, null, '  '));
});

Reading data

Slots

This methods returns the available slots on the system.

pkcs11.slots();

An example response:

{
    "data":
    [
        {
            "slot": 0,
            "description": "eToken 5100",
        },
        {
            "slot": 1,
            "description": "",
        }
    ]
    "success": true    
}

Slots with tokens present

This method is similar the the slots endpoint but only returns a list of slots where a token is present.

pkcs11.slotsWithTokenPresent(callback);

An example response:

{
    "data":
    [
        {
            "slot": 0,
            "description": "eToken 5100"
        }
    ]
    "success": true    
}

Token

This methods returns the token information for a slot.

pkcs11.token(slot_id, callback);

An example response:

{
    "data":
    {
        "slot": 1,
        "label": "Token Label",
        "manufacturerId": "Gemalto",
        "model": "Safenet Token e5100",
        "serialNumber": "1234567890",
        "flags": 7,
        "maxSessionCount": 10,
        "sessionCount": 1,
        "maxRwSessionCount": 5,
        "rwSessionCount": 1,
        "maxPinLength": 20,
        "minPinLength": 8,
        "totalPublicMemory": 10000,
        "freePublicMemory": 5000,
        "totalPrivateMemory": 1000,
        "freePrivateMemory": 50,
        "hardwareVersion": "1",
        "firmwareVersion": "1"
    },
    "success": true    
}

Certificates

This methods allows you to retrieve the certificates from the PKCS #11 token.

// retrieve certificates for token in slot_id 0
pkcs11.certificates(0, false);

Response:

{
  "data": [
    {
      "certificate": "MIIFjjCCA3agAwI...rTBDdrlEWVaLrY+M+xeIctrC0WnP7u4xg==",
      "id": "E8CE05618E79A79825722AE067EC0029851D860D"
    },
    {
      "certificate": "MIIFjjCCA3agAwI...rTBDdrlEWVaLrY+M+xeIctrC0WnP7u4xg==",
      "id": "9F8F1CD68867526B1DF919832219B217282D2B0B"
    }
  ],
  "success": true
}

Signing data

To successfully sign data, we need the following parameters:

  • Slot ID of the token to use

  • Certificate ID of the signing certificate

  • PIN code

  • Hashed data to sign

  • Hashing algorithm used

The slot id can be found using either a call to slots, slotsWithTokenPresent. Once the slot id is found, the certificates can be retrieved with a call to certificates. This then returns the certificate id. Now we can combine this with the PIN code and hashed data + hashing algorithm (SHA1, SHA256, SHA384, SHA512) to create the final signData call:

signData call

Returns signed data for provided input data.

var signDataPayload = {
    slotId: 1,
    certificateId: "9F8F1CD68867526B1DF919832219B217282D2B0B",
    pin: "thepincodeforthetoken",
    data: "hashed data"
    algorithm: "sha256"
}

pkcs11.signData(signDataPayload, callback);

An example response:

{
    "data": "L3BWs7lvoajPg5tC9GEmlSMdXMcDkNDUVVvt+KFqbJbXkJdI3DGoM3IOeXLaeTCxKmJ33/QQApL1nEAMuHY38V41czyswfSdpnqeex3EisXKkiYHeNzt9AXeoxDtsWbGkejKqru6X4xzAy/1SnccjZ2BQB/uUwyfyyweFjbZAGCLfTzWraCxG0ud2c8itsjmsBgXSuGjLqxKIJXbtuX22gd0nc4LeZLA91sxaoNFBCEVlOgJ/oJncFylf6T/Tz9R4VOA9kSf6NB1tAMsFEsihgjZafu/rzlrOfzZw4YY/T32LKY4Y2zNnDhJAucqflZjopGadDvkkSmTvxxEJuE+Bw==",
    "success": true    
}

Error Handling

Error Object

The functions specified are asynchronous and always need a callback function. The callback function will reply with a data object in case of success, or with an error object in case of an error. An example callback:

function callback(err,data) {
    if(err){
        console.log("Error:",JSON.stringify(err, null, '  '));
    }
    else {
        console.log(JSON.stringify(data, null, '  '));
    }
}

The error object returned:

{
  success: false,
  description: "some error description",
  code: "some error code"
}

Last updated