OpenSSL CLI
Introduction
This tutorial provides an overview of how to use the OpenSSL command line interface (CLI) with a Primus HSM. For a comprehensive guide on all available commands and options, please refer to the OpenSSL man page.
Generally, we recommend using native PKCS#11 tools like pkcs11-tool for management functions
such as generating, importing, and deleting objects on the HSM.
These tools has fewer levels of indirection because they use the PKCS#11 APIs directly.
In particular, OpenSSL may not support all features, or may show errors despite the operation having succeeded.
See this tutorial for how to use the native PKCS#11 tools.
Referencing objects
PKCS#11 objects are referenced via the PKCS#11 URI scheme defined by RFC 7512.
Wherever a key is used as an input argument, its PKCS#11 URI can be entered in place of a file name.
When generating a key, the URI can be passed as a pkeyopt (e.g. -pkeyopt "pkcs11_uri:pkcs11:type=private?object=SomeLabel").
In OpenSSL 3.x the "Property" mechanism can be used to limit the selection of different algorithm implementations.
The propquery parameter
is available for many of the commands that are accessible through the CLI.
When setting -propquery "provider=pkcs11" only algorithms that are offered by the OpenSSL PKCS#11 provider will be used.
Finally, recall that the "token label" (in PKCS#11 terms) refers to the partition name on a Primus HSM.
The pkcs11_uri: prefix is only needed for -pkeyopt.
In other arguments, such as -inkey, you need to specify the PKCS#11 URI without this prefix.
Prerequisites
This tutorial assumes that you have fully configured the entire stack of OpenSSL, a provider or an engine, and the PKCS#11 libraries.
Use the following command to check that the provider/engine (depending on which setup you chose) is loaded by OpenSSL:
openssl list -providers
openssl list -engines
Note that the provider-based setup requires OpenSSL 3.x, while the engine-based setup works with both OpenSSL 1.x and 3.x. Depending on which setup you chose, the commands below will differ.
Make sure to use the correct command for your setup!
Common environment variables
All command samples on this page assume that you have defined the following environment variables:
export P11_TOKEN=<YOUR_HSM_PARTITION_NAME>
export P11_PIN=<YOUR_PKCS11_PASSWORD>
export P11_LABEL=<YOUR_PKCS11_KEY_NAME>
Generate a key pair
The key generation commands below are only compatible with OpenSSL 3.x using a provider-based setup.
If you are using OpenSSL 1.x or OpenSSL 3.x with an engine-based setup
you can generate keys with pkcs11-tool.
In fact, we recommend to use pkcs11-tool for OpenSSL 3.x, too, because it has better compatibility.
The following table lists popular supported algorithms and algorithm options:
ALGORITHM | ALGORITHM_OPT |
|---|---|
rsa | rsa_keygen_bits:2048 |
rsa | rsa_keygen_bits:3072 |
rsa | rsa_keygen_bits:4096 |
EC | ec_paramgen_curve:prime256v1 |
EC | ec_paramgen_curve:secp384r1 |
EC | ec_paramgen_curve:secp521r1 |
Ed25519 | - |
Ed448 | - |
ML-DSA-44 | - |
ML-DSA-65 | - |
ML-DSA-87 | - |
Generate the key pair:
export ALGORITHM=<PREFERRED_ALGO>
export ALGORITHM_OPT=<PREFERRED_ALGO_OPTION>
openssl genpkey -propquery "provider=pkcs11" \
-algorithm "${ALGORITHM}" \
${ALGORITHM_OPT:+-pkeyopt} ${ALGORITHM_OPT} \
-pkeyopt "pkcs11_uri:pkcs11:token=${P11_TOKEN};object=${P11_LABEL}?pin-value=${P11_PIN}"
Export a public key
For the provider-based setup:
openssl pkey -propquery "provider=pkcs11" \
-pubin -in "pkcs11:token=${P11_TOKEN};object=${P11_LABEL}?pin-value=${P11_PIN}" \
-outform PEM -out "${P11_LABEL}.pub.pem"
For the engine-based setup:
openssl pkey -engine pkcs11 -inform engine \
-in "pkcs11:type=public;object=${P11_LABEL}" -pubin
Sign data
For the provider-based setup:
openssl pkeyutl -propquery "provider=pkcs11" -sign \
-inkey "pkcs11:token=${P11_TOKEN};object=${P11_LABEL}?pin-value=${P11_PIN}" \
-rawin -in data.txt -out data.txt.sig
For Ed25519, pass the following additional arguments to include a context-string (Ed448ctx currently does not work):
-pkeyopt instance:Ed25519ctx -pkeyopt context-string:hello-world
For Ed25519/Ed448, pass the following additional arguments to use the pre-hashed variant:
-pkeyopt instance:Ed25519ph
# or
-pkeyopt instance:Ed448ph
For verification, pass the same additional arguments.
Verify signature
For the provider-based setup:
openssl pkeyutl -verify \
-pubin -inkey "${P11_LABEL}.pub.pem" \
-rawin -in data.txt -sigfile data.txt.sig
This example assumes that you have exported the public key to a local file, as shown above.
Create a Certificate Signing Request (CSR)
For the provider-based setup:
openssl req -propquery "provider=pkcs11" \
-new -key "pkcs11:token=${P11_TOKEN};object=${P11_LABEL}?pin-value=${P11_PIN}" \
-out "${P11_LABEL}.csr.pem"
For the engine-based setup:
openssl req -engine pkcs11 -keyform engine \
-new -key "pkcs11:object=${P11_LABEL}" \
-out "${P11_LABEL}.csr.pem"
Create a self-signed certificate
To create a self-signed, use the same command as for creating a CSR, but additionally pass the -x509 option:
openssl req -propquery "provider=pkcs11" \
-new -key "pkcs11:token=${P11_TOKEN};object=${P11_LABEL}?pin-value=${P11_PIN}" \
-x509 -out "${P11_LABEL}.crt.pem"
The certificate is not stored on the HSM. The certificate needs to be written to the token explicitly. See the PKCS#11 docs for how to import a certificate.