Skip to content

Firmware signing using PKCS11, with Approval stage

This tutorial will guide the reader through performing an OpenSSL Signing operation based on PKCS11, using the Cryptera KeyServer API.

Web UI

The approval UI is available at https://keyclient..cryptera.com:8300/login

Prerequisites

While most Linux distributions should work, this tutorial is based on Ubuntu 24.04 with the following prerequisites:

For Building - Docker Engine

For Operation - cURL, for HTTP requests - openssl, for command line cryptographic operations - pkcs11-tool, for interfacing with Hardware Security Module. - jq - libengine-pkcs11-openssl - opensc

Building PKCS11 library from Source (Optional)

From the main folder, build and tag the image "libpkcs11ks"

docker build -t libpkcs11ks .

The container can then be used to build the library in a consistent environment. Running the container, will copy the Binary library to ./output directory - in addition a default test is triggered

docker run -v "$(pwd)/output:/output" libpkcs11ks

Authentication

For development an authentication server is provided with a default configuration. When integrating into gitlab or similar, this server is not used, but instead KeyServer is configured to accept the GitLab JSON Web Token (JWT) or similar. For the sake of the tutorial, the default configuration is used.

The demo system comes pre-installed with a number of users configured:

  • adminuser - This user has access to all operations, including Admin operations through the UI.
  • adminclient - This user has access to all operations through the API
  • orderer-client - Able to create pipeline requests and signing for all keys
  • orderer-clientkey# - Able to create pipeline requests and signing for the specified key #, eg. orderer-clientkey1
  • approver-key# - Able to approve signing requests for the specified key #, eg. approver-key2

To get an authentication token from the demo server:

export KSC_ID_TOKEN=$(curl -sS --tlsv1.3 -X POST -H "Authorization: Basic <BASE64-CREDENTIALS>" -d "grant_type=client_credentials" https://authserver.<UNIQUE-ID>.cryptera.com:8300/oauth2/token | jq -r .access_token)
Where is defined as a base64 encoding of username:password (echo -n "adminclient:" | base64) All responses from KeyServer are json encoded, token is stored in access_token.

Available Keys

The list of available keys is available here: https://keyclient..cryptera.com:8100/keys The following approval requirements are set for the default configuration (which can be modified here https://keyclient..cryptera.com:8100/keyadmin):

  • key1 - 0 approvers
  • key2 - 1 approver
  • key3 - 2 approvers
  • key4 - 1 approver

Initiate Pipeline

Before requesting a signing operation, a pipeline must be initiated and approved if required. Please refer to: https://keyserver..cryptera.com:8200/swagger-ui/index.html#/operations/createoperation for API description.

In particular, validms defines the maximum amount of time the operation will be allowed to last. Followed by an array of which keys should be used and the amount if times each key is permitted. Below is an example for requesting 60 seconds approval to use key1 and key4 1 time each: please note that the whole sequence must be completed before approval expires

export KSC_OPERATION_ID=$(curl -sS --tlsv1.3 -X 'POST' \
  'https://keyserver.<UNIQUE-ID>.cryptera.com:8200/api/operations' \
  -H "Authorization: Bearer $KSC_ID_TOKEN" \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "validms": 60000,
  "description": "Building of firmware for XYZ development board",
  "new-keyusagerestrictions": [
    {
      "keyid": "key1",
      "maxusagecount": 1
    },
    {
      "keyid": "key4",
      "maxusagecount": 1
    }
  ]
}' | jq -r .id)
The resulting pipeline operation id must then be approved.

Approval of pipeline operation

Login with a user that has the appropriate approval rights to: https://keyclient..cryptera.com:8100/findOperationForApproval and approve the operation id.

The script / pipeline should poll the operation status API waiting for the operation to be approved ("approved":"true"), before continuing to performing signing operation (which from this point is executed synchrously) Refer to: https://keyserver..cryptera.com:8200/swagger-ui/index.html#/operations/getoperation

Signing

With the pipeline operation being approved the actual signing operation is synchronous. From this point PKCS11 is used as entry point for the KeyServer operations.

Configuring OpenSSL config to work with PKCS11

To get OpenSSL to work with PKCS11 we need to create an customized OpenSSL configuration file (openssl-ks.cnf):

openssl_conf = openssl_init

[openssl_init]
engines = engine_section
providers = provider_sect


[provider_sect]
default = default_sect
base = base_sect

[default_sect]
activate = 1

[base_sect]
activate = 1

[engine_section]
pkcs11 = pkcs11_section

[pkcs11_section]
engine_id = pkcs11
dynamic_path = /usr/lib/x86_64-linux-gnu/engines-3/libpkcs11.so
MODULE_PATH = /opt/app/scripts/libpkcs11keyserver.so

init = 0

The following line should be altered to match the path for our newly built PKCS11 binary:

MODULE_PATH = /opt/app/scripts/libpkcs11keyserver.so

Now to set this as the active configuration file set the following environment variable:

export OPENSSL_CONF=<PATH_TO_ABOVE_FILE>

Specifying variables

The PKCS11 library is inspecting the following variables, and require them to be updated for the specific operation being carried out:

export KSC_API_SERVER="https://keyserver.<UNIQUE-ID>.cryptera.com:8200"
export KSC_OPERATION_ID=$operation_id
export KSC_OPERATION_DESCRIPTION="Building of firmware for XYZ development board"

  • KSC_API_SERVER - Reference to the specific customer KeyServer
  • KSC_OPERATION_ID - The resulting Operation ID given as response to pipeline initiation
  • KSC_OPERATION_DESCRIPTION - Description of changes, presented to the Approver for context
  • KSC_ID_TOKEN - For example the token manually fetched above, or a GitLab JWT

List available keys

It is possible to list all available keys on the HSM using pkcs11-tool (make sure the module path is changed to match):

pkcs11-tool --module /opt/app/libpkcs11keyserver.so --list-objects

Signing with OpenSSL via PKCS11

At this point a signing operation can be carried out using eg. OpenSSL or any application that can rely on PKCS11. can be determined either in the web-ui or from the output of the above command.

openssl dgst -engine pkcs11 -keyform engine  -sign "pkcs11:object=<KEYLABEL>" -sha256 -out <signature_file> <data_file>
In the case, OpenSSL will call PKCS11, which in turn asks KeyServer to perform a signing operation and return the signature.

Verification

It is possible to validate the signing operation by extracting the matching public key and validate the signature against it.

Retrieve the certificate for key1 and convert to PEM:

pkcs11-tool --module $SCRIPT_PATH/build/libkscpkcs11.so --read-object --type cert --label <KEYLABEL> --output-file <certkey.der>
openssl x509 -pubkey -noout -inform DER -in <certkey.der> -out <pubkey.pem> -outform PEM

Verification of signature for data and public key:

openssl dgst -sha256 -verify <publickey.pem> -signature <signature_file> <data_file>

Tutorial script

A script implementing the process described in this tutorial is provided as an example scripts/signandvalidate.sh