Plugin Basics

Look at our demo-page to get an idea how different components of the Kit work.

The plugin API is based on asynchronous operations - promise objects (detailed info see here). A promise must provide a then method to access its value. A promise’s then method accepts two arguments:

  • OnFulfilled - trigger when the promise is fulfilled ("successful");
  • OnRejected - trigger when the promise is rejected (e.g. "completed with an error").

So the universal method to use promise objects is:

Promise.then(onFulfilled, onRejected)

Creating Plugin Object

The first step is to create the plugin object on the webpage. Use the script, that is part of Browser Digital Signature Kit. To create the object, call the script's createFactory function. It has the following parameters:

  • PluginName - the name of the plugin;
  • MimeType - the media type of the data;
  • Hostname - the reverse domain of the native application required for correct interaction of Chrome with the plugin, for example, ru.reaxoft.firewyrmhost;
  • MinVersion (optional parameter) - the minimum required version of the plugin, format 1.0.0.0 (e.g. 1.6.0.0).

After purchasing the Browser Digital Signature Kit you'll receive these values to use in your website.

The createFactory returns the function that creates the plugin object on the page. createFactory has arguments:

  • Function that should be performed when the plugin object is created;
  • Function that should be performed when the plugin object is wasn't created. This function should have error code as the first parameter, error description as the second parameter. The third one and the subsequent are functions that are called depending on the type of error.

When creating the plugin object the following errors are possible:

  • Plugin_not_available - the plugin is not installed (or is not allowed to run). In this case recommend the user to install the plugin. This error occurs if you try to access the plugin version 1.5.0.0 from Firefox version 52 and higher. To solve the problem update the plugin to version 1.6.0.0 and higher. As the third parameter the function waitingPluginCb is indicated that puts the plugin in standby mode;
  • Plugin_not_allowed - the plugin is blocked by the browser. In this case recommend the user to unlock the plugin. As the third parameter the function expectAllowCb is indicated that puts the plugin into the unlocking mode;
  • Chrome_extension_not_available - the Chrome extension required to run the plugin is not installed or is not available. In this case recommend the user to install the extension from the extension page (second parameter). As the third parameter the function expectExtensionCb is indicated that puts the plugin in standby mode;
  • Firefox_extension_not_available - the extension for Firefox (version 52 and higher) required to run the plugin is not installed or available. In this case recommend to install the extension by providing a link to the add-on page - the second parameter (the link is temporarily unavailable). As the third parameter the function expectExtensionCb is passed that puts the plugin in standby mode. As the fourth parameter you can specify the URL to download the extension from the plugin manufacturer's site (Identityblitz.ru). Since the extension can only be installed by explicitly clicking on a link, then as the fifth parameter you must specify a function to install the extension;
  • Plugin_not_valid - the plugin is installed, but it does not work correctly. In this case recommend the user to contact the administrator (the detailed description of the error is transmitted in the second parameter);
  • Plugin_old_version - user uses an older version of the plugin. The current version of the plugin is passed as the second parameter.

Initializing Plugin

After creating plugin object call the plugin.initPKCS11 function to initialize PKCS#11 components. This function takes as the parameter a list of module names (in the form of an array). The list of modules can be found here.

If no modules are specified the plugin initiates all modules.

Upon successful initialization, the function returns an object based on the promise mechanism. The object has functions modules and getCertsForSign.

Getting the List of Modules

To view the list of modules and their status, you need to call the function modules . Example query:

pkcs11.modules.then(onFulfilled, onRejected);

An example of a function response (breaks are given for readability):

 [
   {
      "enable": true,
      "name": "Aladdin R.D. Unified JaCarta"
   },
   {
      "enable": true,
      "name": "Rutoken ECP"
   },
   {
      "enable": false,
      "error": "100:failed to load p11 module",
      "name": "ISBC ESMART"
   }
]

Getting the List of Certificates

To view the list of detected certificates, you need to call the function getCertsForSign. As a parameter you need to specify whether to use the parallel mode of initialized PKCS#11 modules:

  • True - parallel access to modules (recommended mode);
  • False - consecutive access to modules.

The list of certificates is a JavaScript array, the elements are the certificate objects. On the certificate object you can perform the full_info, cms_sign_on_it, and start_signing functions.

Viewing Certificate's Data

To view particular certificate data call the full_info function. It returns information about the certificate as a json object:

  • Sn - serial number of the certificate;
  • Subject - the digital certificate subject info in json "parameter: value" format, where the parameter is the name of the object identifier (OID). All standard object identifiers are given a common notation, for example, CN (Common Name).
  • Issuer - the certificate's issuer info in json "parameter: value" format, where the parameter is the name of the object identifier (OID). All standard object identifiers are given a common notation;
  • Not_before - certificate's validity start time (data type - string in ASN1_TIME format);
  • Not_after - certificate's validity time (data type - string in ASN1_TIME format).

The certificate start / end time in ASN1_TIME format can be converted to standard format using the new Date (ASN1_TIME) function.

Data Signing

Simple Mode

To sign a string using the selected certificate call the function cms_sign_on_it with input parameters:

  • a string to sign;
  • the number of attempts to enter a pin code (for example, the value "1" means that the user has only one attempt;
  • signature type - whether the signature is attached (true) or detached (false).

Function returns a string with a signature in the format CAdES-BES/PKCS#7 attached/detached.

An example of calling the function on the certificate object:

cms_sign_on_it("1234", 3, true).then(function(cms){console.log(cms)});

Advanced mode

Advanced mode allows:

  • signing large strings e.g. files;
  • signing several files without re-requesting the PIN code.

To sign the data using the selected certificate you must first initialize the signer object using the start_signing function on the certificate object. Parameters:

  • signature type - whether the signature is attached (must be passed true) or detached (false);
  • number of attempts to enter the PIN code (e.g. the value "1" means that the user has only one attempt, if it fails function returns an error).

The following methods will be available on the signer object:

  • Add_data_in_hex(hexDataString) - input data as hex string;
  • Add_data_in_base64(base64DataString) - input data as base64 string;
  • Add_data_in_string(stringData) - input data as utf-8 string;
  • Finish() - finalizes the signature and returns it in the CAdES-BES/PKCS#7 format.

This function returns a string with a signature in the CAdES-BES/PKCS#7 attached/detached format.

An example of a command that allows you to sign a string:

signer.add_data_in_string("1234").then(function(res){ return signer.finish();}).then(function(cms){console.log(cms)});

In this command "1234" is the string you want to sign.

After the finalization the signer object is returned to its original state. Within a session you can reuse it to sign other data, for example, a new file. In this case, the PIN code will not be requested again.

When signing large files, it is recommended to sequentially read and sign fragments of the file. As an example, you can use the following function:

   function readFileByChunk(file, cbToRead, cbToFinish) {
       var fileSize   = file.size;
       var chunkSize  = 1024*1024; // bytes
       var offset     = 0;
       var chunkReaderBlock = null;
       var self       = this;

       var readEventHandler = function(evt) {
           if (evt.target.error == null) {
               cbToRead(evt.target.result, offset, fileSize);
               offset += evt.target.result.byteLength;
           } else {
               console.error("Read error: " + evt.target.error);
               showError("Ошибка чтения файла: " + evt.target.error);
               return;
           }
           if (offset >= fileSize) {
               cbToFinish()
               return;
           }

           // to the next chunk
           chunkReaderBlock(offset, chunkSize, file);
       }

       chunkReaderBlock = function(_offset, _chunkSize, _file) {
         var r = new FileReader();
         if (_file.slice) {
           var blob = _file.slice(_offset, _chunkSize + _offset);
         } else if (_file.webkitSlice) {
           var blob = _file.webkitSlice(_offset, _chunkSize + _offset);
         } else if (_file.mozSlice) {
           var blob = _file.mozSlice(_offset, _chunkSize + _offset);
         }
         r.onload = readEventHandler;
         r.readAsArrayBuffer(blob);
       }

       // start reading the first block
       chunkReaderBlock(offset, chunkSize, file);
   }

Please note that the signing process is done locally. So if you want to send the file and the signature to the server it is necessary to implement this logic on the web page.