step-ca is an online certification authority with strong support for automatic certificate management. It supports protection of cryptographic keys using Hardware Security Modules like the SmartCard-HSM.

The SmartCard-HSM integrates with step-ca using PKCS#11. step-ca does not support PKCS#11 by default. It needs to be compiled with CGO or installed from the step-ca:hsm Docker image. An alternative is to install the step-kms-plugin, that supports p11-kit to discover and access PKCS#11 modules. The step-kms-plugin must be copied into the plugins directory of the step data directory (The output of step path).

Make sure to install OpenSC and p11-kit. p11tool should list the URL for your SmartCard-HSM:

$ p11tool --list-tokens
Token 1:
        URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=www.CardContact.de;serial=UTTESTTRM;token=SmartCard-HSM
        Label: SmartCard-HSM
        Type: Hardware token
        Flags: RNG, Requires login
        Manufacturer: www.CardContact.de
        Model: PKCS#15 emulated
        Serial: UTTESTTRM
        Module: opensc-pkcs11.so

The important part is the serial in the URL. That is sufficient to uniquely identify the device. Store the URL as environment variable.

$ export PKCS_URI='pkcs11:serial=UTTESTTRM?pin-value=648219'

The PIN value must be appended as pin-value attribute.

It is recommended to generate your root and intermediate ca keys using the Key Manager in the Smart Card Shell. That way you can specify additional key attributes and which key domain the key should be created in.

The Key Identifier uniquely identifies the key in the PKCS#11 URL. This is the same value found in the Subject Key Identifier or Authority Key Identifier certificate extension.

Use the step-kms-plugin to check that the key can be found.

$ step-kms-plugin --kms "$PKCS_URI" key "pkcs11:id=92274B2B6786D0E30B5F3C29BD4AC9D73572D8F7"
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYk45KydH8gkwLcE2WDxRiV6efL54
SuLP424muzNwr+nx5e0WHM8sl6j3twUSQK9GkELksCLJ24j8RbmeDoKYkg==
-----END PUBLIC KEY-----

Now you can create the root certificate

$ step certificate create --profile root-ca --kms "$PKCS_URI" \
--key "pkcs11:id=92274B2B6786D0E30B5F3C29BD4AC9D73572D8F7" "My Root CA" root_ca.crt

and the certificate of the intermediate ca

$ step certificate create --profile intermediate-ca --kms "$PKCS_URI" \
--ca-kms "$PKCS_URI" --ca root_ca.crt \
--ca-key "pkcs11:id=92274B2B6786D0E30B5F3C29BD4AC9D73572D8F7"\
--key "pkcs11:id=6DCA696CA82A3AAD3519187C4E86FDA2E1E46F1E" "My Sub CA" intermediate_ca.crt

The certificates must be copied into the certs directory of your step data (e.g. .step/certs) and the key must be referenced in the config/ca.json file:

{
    "root": "/home/step/.step/certs/root_ca.crt",
    "federatedRoots": null,
    "crt": "/home/step/.step/certs/intermediate_ca.crt",
    "key": "pkcs11:id=6DCA696CA82A3AAD3519187C4E86FDA2E1E46F1E",
    "kms": {
        "type": "pkcs11",
        "uri": "pkcs11:serial=UTTESTTRM?pin-value=648219"
    },

Next start the step-ca daemon

$ step-ca 
badger 2026/03/22 00:02:29 INFO: All 1 tables opened in 0s
badger 2026/03/22 00:02:29 INFO: Replaying file id: 0 at offset: 3199
badger 2026/03/22 00:02:29 INFO: Replay took: 5.901µs
2026/03/22 00:02:30 Building new tls configuration using step-ca x509 Signer Interface
2026/03/22 00:02:30 Starting Smallstep CA/0.30.1-2-g6e8ec614 (linux/amd64)
2026/03/22 00:02:30 Documentation: https://u.step.sm/docs/ca
2026/03/22 00:02:30 Community Discord: https://u.step.sm/discord
2026/03/22 00:02:30 Config file: /home/step/.step/config/ca.json
2026/03/22 00:02:30 The primary server URL is https://ruccola.mi.cardcontact.de:8443
2026/03/22 00:02:30 Root certificates are available at https://ruccola.mi.cardcontact.de:8443/roots.pem
2026/03/22 00:02:30 X.509 Root Fingerprint: 40816a4b40123447f0302f0d527b8c2ec49439f80c3b986b10ee2ca8108938d2
2026/03/22 00:02:30 Serving HTTPS on :8443 ...

and start issuing certificates

$ step ca certificate svc.example.com svc.crt svc.key
✔ Provisioner: me@cardcontact.de (JWK) [kid: wv9uc_IglrFOmCzSIiTqycwGqr5YQropoLSTP4h1xJA]
Please enter the password to decrypt the provisioner key: 
✔ CA: https://ruccola.mi.cardcontact.de:8443
✔ Certificate: svc.crt
✔ Private Key: svc.key

More details can be found in Cryptographic Protection on the smallstep website.