How-To: Use OpenSSL 3.x with PKCS#11 for Signing¶
This guide explains how to use the Cryptera Keyserver PKCS#11 library with OpenSSL 3.x
This setup allows you to perform signing using keys stored inside the Keyserver’s HSM without exposing private key material.
Production Authentication Required
This workflow describes production-grade signing using OpenSSL 3.x and PKCS#11.
All production deployments must authenticate using an external OAuth2/OIDC identity provider such as
Azure Entra ID, GitLab OIDC, GitHub Actions OIDC, or Keycloak.
The internal Authserver from the demo environment must not be used for production signing.
Internal Authserver for Testing Only
You may use the Keyserver’s internal Authserver while testing this guide on a demo deployment.
In production, authentication must always be handled by your organization’s identity provider
to ensure MFA enforcement, account lifecycle management, and compliance logging.
Prerequisites¶
While most Linux distributions should work, this tutorial is based on Ubuntu 24.04 with the following prerequisites:
For building Keyserver PKCS#11 Library, libpkcs11ks
- Docker
- (Cryptera Keyserver PKCS#11 library)
For operation
- cURL, for HTTP requests
- openssl, for command line cryptographic operations
- pkcs11-tool (opensc), for interfacing with Hardware Security Module.
- jq
- libengine-pkcs11-openssl
Building libpkcs11ks.so¶
Build the library from source and place it somewhere readable by your tooling.:
Fetch the latest version of the source code for the library:
From the libpkcs11ks folder:
The container can then be used to build the library in a consistent environment. Running the container, will copy the Binary library to ./output directorySave the libpkcs11ks.so artifact from the output folder for later use.
Configuration of PKCS11 Library (libpkcs11ks.so)¶
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-security.io"
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, GitLab JWT, etc.
5. Create an OpenSSL 3.x Provider Configuration¶
To get OpenSSL to work with PKCS11,create a 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/libpkcs11ks.so
init = 0
The following line should be altered to match the path for the newly built PKCS11 binary:
Now to set this as the active configuration file set the following environment variable:
6. Find the Key via PKCS#11 URI¶
(Requires valid KSC_ID_TOKEN, see below) List keys:
You will see:
Build the key URI:
Signing using OpenSSL¶
1. Authenticate and Obtain an OAuth2 Token¶
Note: This stage is for trial setup only. For production deployments, refer to the Authentication Providers section.
Encode credentials:
Request token:
export KSC_ID_TOKEN=$(
curl -sS --tlsv1.3 \
-X POST \
-H "Authorization: Basic $AUTH_CREDS" \
-d "grant_type=client_credentials" \
https://authserver.<UNIQUE-ID>.cryptera-security.io/oauth2/token \
| jq -r .access_token
)
Verify:
2. Create an Operation¶
OpenSSL operations require the Keyserver to know:
- Which key is allowed to be used
- How many times
- For how long
Create a short-lived operation:
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 "Content-Type: application/json" \
-d "{
\"validms\": 60000,
\"description\": \"OpenSSL provider signing\",
\"new-keyusagerestrictions\": [
{ \"keyid\": \"key1\", \"maxusagecount\": 1 }
]
}" | jq -r .id
)
Display it:
3. Approve the Operation (If Required)¶
If the key requires approvals:
- Go to
- Log in as an approver
- Approve the operation
Otherwise skip this step.
4. Update per operation description¶
5. Sign Data¶
Create data to sign:
At this point a signing operation can be carried out using OpenSSL or any application that can rely on PKCS11.
openssl dgst -engine pkcs11 -keyform engine -sign "pkcs11:object=<KEYLABEL>" -sha256 -out <signature_file> <data_file>
openssl dgst \
-engine pkcs11 \
-keyform engine \
-sign "pkcs11:object=key1" \
-sha256 \
-out signature.bin \
data.bin
OpenSSL will:
- Load the pkcs11 engine
- Use the Keyserver PKCS#11 module
- Send a request to the Keyserver
- The Keyserver performs signing inside the HSM
- The signature is returned
8. Extract the Certificate for Verification¶
Export Certificate via PKCS#11:
pkcs11-tool \
--module /path/to/libpkcs11ks.so \
--read-object --type cert --label <KEYLABEL> \
--output-file <KEYLABEL>.der
Convert to PEM:
Extract the public key from the certificate
9. Verify the Signature¶
If successful:
Summary¶
Summary¶
This guide covered how to use OpenSSL 3.x with the Cryptera Keyserver PKCS#11 library for secure signing operations. You learned how to:
- Build and configure the libpkcs11ks PKCS#11 library
- List and identify HSM-protected keys using pkcs11-tool
- Authenticate to the Keyserver and obtain an OAuth2 token
- Create and approve signing operations with usage restrictions
- Perform signing using OpenSSL with PKCS#11 engine and provider
- Export certificates and public keys for verification
- Verify signatures using OpenSSL
By following these steps, you can securely sign data using keys stored in the Keyserver HSM, ensuring private key material remains protected and all operations are authenticated and auditable.