Set up OpenSSL for PKCS#11 tokens

This guide will show you how to set up OpenSSL to automatically use PKCS#11 modules on the system.

This will allow you to perform cryptographic operations (such as certificate signing for managing a private CA) using a secure cryptographic token from which private key material cannot be extracted.

PKCS#11 is a standard that defines generic cryptographic operations backed by a hardware token (smartcard, HSM, TPM) or even by software (SoftHSM2). The manufacturer of your token will typically provide a PKCS#11 module for you to use with your applications and everything is (mostly) transparent.

The OpenSC project provides among other thing a PKCS#11 module that works with many different kinds of tokens, including the PIV or PGP tokens such as the ones exposed by a Yubikey.

For more advanced operations on a Yubikey, the ykcs11 module is necessary. These operations include fetching attestation certificates or using the retired key slots.

For that purpose we are going to use p11-kit which is a library that aggregates different PKCS#11 modules under a unified interface, and it additionally provides a proxy PKCS#11 module for use with applications that do not interface with p11-kit natively such as the OpenSSL PKCS#11 provider.

All these operations work on Linux and MacOS (although the paths and commands will need to be adapted); a Debian/Ubuntu distribution will be thereafter assumed.

Installation of p11-kit and module creation

Install the required packages:

sudo apt install openssl pkcs11-provider opensc opensc-pkcs11 p11-kit p11-kit-modules

OpenSC comes with a pre-defined p11-kit module already, so we just need to define a module for ykcs11 by creating the folder /etc/pkcs11/modules then the file /etc/pkcs11/modules/ykcs11.module with the following contents:

module: /usr/lib/x86_64-linux-gnu/libykcs11.so

Replace the path to libykcs11.so if it is in a different location on your distribution.

Now if you run p11-kit list-modules you should see something similar to the following, if you have a Yubikey plugged in:

module: p11-kit-trust
    path: /usr/lib/aarch64-linux-gnu/pkcs11/p11-kit-trust.so
    uri: pkcs11:library-description=PKCS%2311%20Kit%20Trust%20Module;library-manufacturer=PKCS%2311%20Kit
    library-description: PKCS#11 Kit Trust Module
    library-manufacturer: PKCS#11 Kit
    library-version: 0.26
    token: System Trust
        uri: pkcs11:model=p11-kit-trust;manufacturer=PKCS%2311%20Kit;serial=1;token=System%20Trust
        manufacturer: PKCS#11 Kit
        model: p11-kit-trust
        serial-number: 1
        hardware-version: 0.26
        flags:
              write-protected
              token-initialized
module: opensc-pkcs11
    path: /usr/lib/aarch64-linux-gnu/pkcs11/opensc-pkcs11.so
    uri: pkcs11:library-description=OpenSC%20smartcard%20framework;library-manufacturer=OpenSC%20Project
    library-description: OpenSC smartcard framework
    library-manufacturer: OpenSC Project
    library-version: 0.27
    token: LUKS key
        uri: pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=1122334455667788;token=LUKS%20key
        manufacturer: piv_II
        model: PKCS#15 emulated
        serial-number: 1122334455667788
        flags:
              rng
              login-required
              user-pin-initialized
              token-initialized
module: ykcs11
    path: /usr/lib/aarch64-linux-gnu/libykcs11.so
    uri: pkcs11:library-description=PKCS%2311%20PIV%20Library%20%28SP-800-73%29;library-manufacturer=Yubico%20%28www.yubico.com%29
    library-description: PKCS#11 PIV Library (SP-800-73)
    library-manufacturer: Yubico (www.yubico.com)
    library-version: 2.73
    token: YubiKey PIV #39779214
        uri: pkcs11:model=YubiKey%20YK5;manufacturer=Yubico%20%28www.yubico.com%29;serial=12345678;token=YubiKey%20PIV%20%2312345678
        manufacturer: Yubico (www.yubico.com)
        model: YubiKey YK5
        serial-number: 39779214
        hardware-version: 1.0
        firmware-version: 5.80
        flags:
              rng
              login-required
              user-pin-initialized
              token-initialized

Setup of OpenSSL

The OpenSSL setup can be made persistent by changes to the system-wide configuration as we will see in this section; but it is possible to specify the provider on the command-line as we will see in the Extra: specify the provider on the command-line section.

At the end of /etc/ssl/openssl.cnf make sure that files from /etc/ssl/openssl.cnf.d are being included by having this line:

.include /etc/ssl/openssl.cnf.d

Then create /etc/ssl/openssl.cnf.d/pkcs11.cnf with the following contents:

[openssl_init]
providers = provider_sect

[provider_sect]
default = default_sect
pkcs11 = pkcs11_sect

[pkcs11_sect]
module = /usr/lib/x86_64-linux-gnu/ossl-modules/pkcs11.so
pkcs11-module-path = /usr/lib/x86_64-linux-gnu/p11-kit-proxy.so
activate = 1

[default_sect]
activate = 1

Now OpenSSL will be able to find objects on PKCS#11 tokens, either through OpenSC or through ykcs11.

Note: if you use an openssl command with a -config option, make sure to add .include /etc/ssl/openssl.cnf as the first line so the PKCS#11 provider can be found.

If using p11-kit is not desirable, you can directly load either libykcs11.so or opensc-pkcs11.so in the configuration; the module to load can always be overriden by the PKCS11_PROVIDER_MODULE environment variable as we will see in the section Extra: specify the provider on the command-line.

Example

Assuming a Yubikey 5 with recent firmware is plugged in, we can show the attestation certificate:

openssl x509 \
  -in 'pkcs11:model=YubiKey%20YK5;id=%19;object=X.509%20Certificate%20for%20PIV%20Attestation;type=cert' \
  -noout -text

You should get something similar to the following:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            ae:85:f9:6d:89:4e:a9:dc
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=Yubico PIV Attestation B2 1
        Validity
            Not Before: Aug  1 00:00:00 2025 GMT
            Not After : Dec 31 23:59:59 9999 GMT
        Subject: CN=Yubico PIV Attestation
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:ac:d3:63:a1:8e:0f:ab:0e:a6:14:46:66:5d:17:
                    cf:b8:c9:98:6a:2f:5f:12:e7:dc:60:4c:10:ce:92:
                    80:83:d8:3b:d1:58:fe:9b:41:6c:18:a4:34:8e:3a:
                    2b:bc:12:d6:75:85:bb:5b:d5:ac:a1:85:ad:6e:c7:
                    fb:fa:e9:5b:65:21:fb:be:26:a7:54:32:06:a9:d3:
                    cf:bf:bc:8d:d0:2b:c4:4c:7b:22:42:79:2e:18:5c:
                    55:39:8b:87:55:a7:86:11:01:1c:1d:2a:5d:17:b6:
                    c7:96:1a:29:55:7a:fb:ea:11:a5:f8:f2:12:a0:21:
                    67:4b:88:7e:59:10:36:97:ee:72:fa:59:02:7d:4c:
                    a9:91:b0:42:fd:53:f6:78:7a:a2:96:2a:ad:3f:ca:
                    ac:ea:f8:20:fb:a3:01:eb:7f:64:96:81:5a:3c:5f:
                    be:15:e1:02:76:b4:5d:67:02:2e:7b:4b:7f:e3:32:
                    da:37:e9:0a:28:7e:f9:82:ed:de:3f:36:89:b9:19:
                    5a:13:9e:db:2c:d5:6d:23:a7:32:c3:04:7b:56:95:
                    5b:e3:60:68:8a:d5:ab:81:da:79:85:56:b5:a3:91:
                    7e:ab:92:f5:60:83:a6:53:74:2f:71:48:1e:f3:68:
                    d4:8e:c3:a9:0b:10:e4:7a:45:62:09:18:42:22:e0:
                    3b:af
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            1.3.6.1.4.1.41482.3.3: 
                ...
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        a8:a9:5d:ac:3c:2e:7b:1a:c2:24:5d:69:c8:35:84:76:af:19:
        52:60:02:e0:e4:cc:4a:12:6f:b6:3c:4a:fc:c6:dd:fa:72:fa:
        46:c1:03:34:9d:89:c7:18:67:29:25:19:aa:e6:f7:e3:aa:68:
        95:1f:5b:66:dc:d3:81:9d:6d:92:20:c7:1f:94:92:57:69:00:
        00:3c:b8:d9:5b:a1:09:14:51:71:61:25:bc:db:2c:ba:01:05:
        d2:0a:bc:8e:26:c2:f5:c3:0a:ab:16:a4:fa:1c:1d:0e:14:ce:
        3c:07:db:d0:ad:c0:1a:9c:3f:82:35:5d:17:a7:e4:f9:16:79:
        51:25:d3:75:60:99:be:dc:a9:90:7c:a7:0a:3b:b2:a3:89:17:
        bc:c9:c7:c3:be:76:f7:18:94:ae:5b:e3:66:28:5a:6b:e1:64:
        a9:92:8d:51:a9:6d:cd:1d:64:18:5c:be:6e:ca:0f:ed:e9:e3:
        f4:81:ef:35:82:3d:f6:70:d0:5b:5c:24:d2:29:6d:79:67:e2:
        2e:9d:78:0d:48:9b:48:dc:e5:c1:20:8e:44:19:2d:1d:8f:63:
        05:43:0c:5e:d9:06:cd:78:53:54:27:5d:ec:b2:43:ed:61:a0:
        fa:85:53:65:0d:13:c2:1d:d6:d4:2f:d0:a3:69:5f:c7:f5:59:
        da:42:f5:b8

Extra: specify the provider on the command-line

If it is not possible or desirable to change the system-wide OpenSSL configuration or to install p11-kit, you can specify the provider on the command-line in most openssl commands, for instance:

PKCS11_PROVIDER_MODULE=/usr/lib/x86_64-linux-gnu/libykcs11.so openssl x509 \
  -provider default -provider /usr/lib/x86_64-linux-gnu/ossl-modules/pkcs11.so \
  -in 'pkcs11:model=YubiKey%20YK5;id=%19;object=X.509%20Certificate%20for%20PIV%20Attestation;type=cert' \
  -noout -text

links