1.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") 2.. 3.. SPDX-License-Identifier: MPL-2.0 4.. 5.. This Source Code Form is subject to the terms of the Mozilla Public 6.. License, v. 2.0. If a copy of the MPL was not distributed with this 7.. file, you can obtain one at https://mozilla.org/MPL/2.0/. 8.. 9.. See the COPYRIGHT file distributed with this work for additional 10.. information regarding copyright ownership. 11 12.. _pkcs11: 13 14PKCS#11 (Cryptoki) Support 15~~~~~~~~~~~~~~~~~~~~~~~~~~ 16 17Public Key Cryptography Standard #11 (PKCS#11) defines a 18platform-independent API for the control of hardware security modules 19(HSMs) and other cryptographic support devices. 20 21PKCS#11 uses a "provider library": a dynamically loadable 22library which provides a low-level PKCS#11 interface to drive the HSM 23hardware. The PKCS#11 provider library comes from the HSM vendor, and it 24is specific to the HSM to be controlled. 25 26BIND 9 accesses PKCS#11 libraries via OpenSSL extensions. The extension for 27OpenSSL 3 and newer is `pkcs11-provider`_; for older OpenSSL versions, 28engine_pkcs11 from the `OpenSC`_ project can be used. 29 30.. _`pkcs11-provider`: https://github.com/latchset/pkcs11-provider 31.. _OpenSC: https://github.com/OpenSC/libp11 32 33In both cases the extension is dynamically loaded into OpenSSL and the HSM is 34operated indirectly; any cryptographic operations not supported by the HSM can 35be carried out by OpenSSL instead. 36 37Prerequisites 38^^^^^^^^^^^^^ 39 40See the documentation provided by the HSM vendor for information about 41installing, initializing, testing, and troubleshooting the HSM. 42 43Building SoftHSMv2 44^^^^^^^^^^^^^^^^^^ 45 46SoftHSMv2, the latest development version of SoftHSM, is available from 47https://github.com/opendnssec/SoftHSMv2. It is a software library 48developed by the OpenDNSSEC project (https://www.opendnssec.org) which 49provides a PKCS#11 interface to a virtual HSM, implemented in the form 50of an SQLite3 database on the local filesystem. It provides less security 51than a true HSM, but it allows users to experiment with native PKCS#11 52when an HSM is not available. SoftHSMv2 can be configured to use either 53OpenSSL or the Botan library to perform cryptographic functions, but 54when using it for native PKCS#11 in BIND, OpenSSL is required. 55 56By default, the SoftHSMv2 configuration file is ``prefix/etc/softhsm2.conf`` 57(where ``prefix`` is configured at compile time). This location can be 58overridden by the SOFTHSM2_CONF environment variable. The SoftHSMv2 59cryptographic store must be installed and initialized before using it 60with BIND. 61 62:: 63 64 $ cd SoftHSMv2 65 $ configure --with-crypto-backend=openssl --prefix=/opt/pkcs11/usr 66 $ make 67 $ make install 68 $ /opt/pkcs11/usr/bin/softhsm-util --init-token 0 --slot 0 --label softhsmv2 69 70OpenSSL 1.x.x With engine_pkcs11 71^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 72 73OpenSSL engine-based PKCS#11 uses the engine_pkcs11 OpenSSL engine from the libp11 project. 74 75engine_pkcs11 tries to fit the PKCS#11 API within the engine API of OpenSSL. 76That is, it provides a gateway between PKCS#11 modules and the OpenSSL engine 77API. One has to register the engine with OpenSSL and one has to provide the 78path to the PKCS#11 module which should be gatewayed to. This can be done by 79editing the OpenSSL configuration file, by engine specific controls, or by using 80the p11-kit proxy module. 81 82It is recommended, that libp11 >= 0.4.12 is used. 83 84For more detailed instructions, including examples, we recommend reading: 85 86https://gitlab.isc.org/isc-projects/bind9/-/wikis/BIND-9-PKCS11 87 88When using engine_pkcs11, be sure to pass the `-E pkcs11` argument to all BIND 89binaries that potentially use the keys, to activate the engine support. 90 91Even though OpenSSL 3 has compatibility support for Engine API, its use is not 92recommended due to bugs in OpenSSL and libp11. 93 94It is not possible to generate new keys via engine_pkcs11, so its use 95is not recommended in a ``dnssec-policy`` setup. However, it is 96possible to put previously generated keys in the ``key-directory`` and let the 97key manager select those keys when a key rollover is started. 98 99Configuring engine_pkcs11 100^^^^^^^^^^^^^^^^^^^^^^^^^ 101 102The canonical documentation for configuring engine_pkcs11 is in the 103`libp11/README.md`_ file, but a sample working configuration is included 104here for the user's convenience: 105 106.. _`libp11/README.md`: https://github.com/OpenSC/libp11/blob/master/README.md#pkcs-11-module-configuration 107 108In our example, we use a custom copy of OpenSSL configuration, 109driven by an environment variable called OPENSSL_CONF. 110First, copy the global OpenSSL configuration (often found in 111``etc/ssl/openssl.conf``) and customize it to use engine_pkcs11. 112 113:: 114 115 cp /etc/ssl/openssl.cnf /opt/bind9/etc/openssl.cnf 116 117Then, export the environment variable: 118 119:: 120 121 export OPENSSL_CONF=/opt/bind9/etc/openssl.cnf 122 123Then add the following line at the top of the file, before any sections (in square 124brackets) are defined: 125 126:: 127 128 openssl_conf = openssl_init 129 130Make sure there are no other 'openssl_conf = ...' lines in the file. 131 132Add the following lines at the bottom of the file: 133 134:: 135 136 [openssl_init] 137 engines=engine_section 138 139 [engine_section] 140 pkcs11 = pkcs11_section 141 142 [pkcs11_section] 143 engine_id = pkcs11 144 dynamic_path = <PATHTO>/pkcs11.so 145 MODULE_PATH = <FULL_PATH_TO_HSM_MODULE> 146 # if automatic logging to the token is needed, PIN can be specified as below 147 #PIN = 1234 148 init = 0 149 150Enabling the OpenSSL Engine in BIND Commands 151^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 152 153When using OpenSSL Engine-based PKCS#11, the "engine" to be used by OpenSSL can be 154specified in :iscman:`named` and in all of the BIND ``dnssec-*`` tools by using the ``-E 155<engine>`` command-line option. This engine name matches the ``engine_id`` in the 156``openssl.cnf`` created in previous section. 157 158The zone signing commences as usual, with only one small difference: we need to 159provide the name of the OpenSSL engine using the -E command-line option. 160 161:: 162 163 dnssec-signzone -E pkcs11 -S -o example.net example.net 164 165 166OpenSSL 3 With pkcs11-provider 167^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 168 169OpenSSL provider-based PKCS#11 uses the pkcs11-provider project. 170 171pkcs11-provider tries to fit the PKCS#11 API within the Provider API of OpenSSL; 172that is, it provides a gateway between PKCS#11 modules and the OpenSSL Provider 173API. The engine must be registered with OpenSSL and the 174path to the PKCS#11 module gateway must be provided. This can be done by 175editing the OpenSSL configuration file, by engine-specific controls, or by using 176the p11-kit proxy module. 177 178The pkcs11-provider git commit 1792e8c26b4157fd21422c66f0b4d7b26cf8c320570 from October 2, 2023 or later must be used. 180 181BIND support for pkcs11-provider is built in; with pcks11-provider, the -E command-line option 182explained above should not be used. 183 184Configuring pkcs11-provider 185^^^^^^^^^^^^^^^^^^^^^^^^^^^ 186 187The canonical documentation for configuring pkcs11-provider is in the 188`provider-pkcs11.7`_ manual page, but a copy of a working configuration is 189provided here for convenience: 190 191.. _`provider-pkcs11.7`: https://github.com/latchset/pkcs11-provider/blob/main/docs/provider-pkcs11.7.md 192 193In this example, we use a custom copy of OpenSSL configuration, 194driven by an environment variable called OPENSSL_CONF. First, copy the 195global OpenSSL configuration (often found in 196``etc/ssl/openssl.conf``) and customize it to use pkcs11-provider. 197 198:: 199 200 cp /etc/ssl/openssl.cnf /opt/bind9/etc/openssl.cnf 201 202Next, export the environment variable: 203 204:: 205 206 export OPENSSL_CONF=/opt/bind9/etc/openssl.cnf 207 208Then add the following line at the top of the file, before any sections (in square 209brackets) are defined: 210 211:: 212 213 openssl_conf = openssl_init 214 215Make sure there are no other 'openssl_conf = ...' lines in the file. 216 217Add the following lines at the bottom of the file: 218 219:: 220 221 [openssl_init] 222 providers = provider_init 223 224 [provider_init] 225 default = default_init 226 pkcs11 = pkcs11_init 227 228 [default_init] 229 activate = 1 230 231 [pkcs11_init] 232 module = <PATHTO>/pkcs11.so 233 pkcs11-module-path = <FULL_PATH_TO_HSM_MODULE> 234 # bind uses the digest+sign api. this is broken with the default load behaviour, 235 # but works with early load. see: https://github.com/latchset/pkcs11-provider/issues/266 236 pkcs11-module-load-behavior = early 237 # no-deinit quirk is needed if you use softhsm2 238 #pkcs11-module-quirks = no-deinit 239 # if automatic logging to the token is needed, PIN can be specified as below 240 # the file referenced should contain just the PIN 241 #pkcs11-module-token-pin = file:/etc/pki/pin.txt 242 activate = 1 243 244Key Generation 245^^^^^^^^^^^^^^ 246 247HSM keys can now be created and used. We are assuming that 248BIND 9 is already installed, either from a package or from the sources, and the 249tools are readily available in the ``$PATH``. 250 251For generating the keys, we are going to use ``pkcs11-tool`` available from the 252OpenSC suite. On both DEB-based and RPM-based distributions, the package is 253called opensc. 254 255We need to generate at least two RSA keys: 256 257:: 258 259 pkcs11-tool --module <FULL_PATH_TO_HSM_MODULE> -l -k --key-type rsa:2048 --label example.net-ksk --pin <PIN> 260 pkcs11-tool --module <FULL_PATH_TO_HSM_MODULE> -l -k --key-type rsa:2048 --label example.net-zsk --pin <PIN> 261 262Remember that each key should have unique label and we are going to use that 263label to reference the private key. 264 265Convert the RSA keys stored in the HSM into a format that BIND 9 understands. 266The :iscman:`dnssec-keyfromlabel` tool from BIND 9 can link the raw keys stored in the 267HSM with the ``K<zone>+<alg>+<id>`` files. 268 269The OpenSSL engine name (``pkcs11``) must be provided if using the engine and 270the algorithm (``RSASHA256``). The key is referenced with the PKCS#11 URI scheme; it 271can contain the PKCS#11 token label (we assume that it has been initialized as bind9), 272the PKCS#11 object label (called "label" when generating the keys using ``pkcs11-tool``), 273and the HSM PIN. Refer to :rfc:`7512` for the full PKCS#11 URI specification. 274 275Convert the KSK: 276 277:: 278 279 dnssec-keyfromlabel -E pkcs11 -a RSASHA256 -l "pkcs11:token=bind9;object=example.net-ksk;pin-value=0000" -f KSK example.net 280 281and ZSK: 282 283:: 284 285 dnssec-keyfromlabel -E pkcs11 -a RSASHA256 -l "pkcs11:token=bind9;object=example.net-zsk;pin-value=0000" example.net 286 287NOTE: a PIN stored on disk can be used by specifying ``pin-source=<path_to>/<file>``, e.g: 288 289:: 290 291 (umask 0700 && echo -n 0000 > /opt/bind9/etc/pin.txt) 292 293and then use in the label specification: 294 295:: 296 297 pin-source=/opt/bind9/etc/pin.txt 298 299Confirm that there is one KSK and one ZSK present in the current directory: 300 301:: 302 303 ls -l K* 304 305The output should look like this (the second number will be different): 306 307:: 308 309 Kexample.net.+008+31729.key 310 Kexample.net.+008+31729.private 311 Kexample.net.+008+42231.key 312 Kexample.net.+008+42231.private 313 314A note on generating ECDSA keys: there is a bug in libp11 when looking up a key. 315That function compares keys only on their ID, not the label, so when looking up 316a key it returns the first key, rather than the matching key. To work around 317this when creating ECDSA keys, specify a unique ID: 318 319:: 320 321 ksk=$(echo "example.net-ksk" | openssl sha1 -r | awk '{print $1}') 322 zsk=$(echo "example.net-zsk" | openssl sha1 -r | awk '{print $1}') 323 pkcs11-tool --module <FULL_PATH_TO_HSM_MODULE> -l -k --key-type EC:prime256v1 --id $ksk --label example.net-ksk --pin <PIN> 324 pkcs11-tool --module <FULL_PATH_TO_HSM_MODULE> -l -k --key-type EC:prime256v1 --id $zsk --label example.net-zsk --pin <PIN> 325 326 327Running :iscman:`named` With Automatic Zone Re-signing 328^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 329 330The zone can also be signed automatically by :iscman:`named`. Again, we need to provide 331the name of the OpenSSL engine using the :option:`-E <named -E>` command-line option, 332if using OpenSSL 1.x.x with engine_pkcs11; this is not needed when using OpenSSL 3.x.x providers. 333 334:: 335 336 named -E pkcs11 -c named.conf 337 338The logs should have lines like: 339 340:: 341 342 Fetching example.net/RSASHA256/31729 (KSK) from key repository. 343 DNSKEY example.net/RSASHA256/31729 (KSK) is now published 344 DNSKEY example.net/RSA256SHA256/31729 (KSK) is now active 345 Fetching example.net/RSASHA256/42231 (ZSK) from key repository. 346 DNSKEY example.net/RSASHA256/42231 (ZSK) is now published 347 DNSKEY example.net/RSA256SHA256/42231 (ZSK) is now active 348 349For :iscman:`named` to dynamically re-sign zones using HSM keys, 350and/or to sign new records inserted via nsupdate, :iscman:`named` must 351have access to the HSM PIN. In OpenSSL-based PKCS#11, this is 352accomplished by placing the PIN into the ``openssl.cnf`` file (in the above 353examples, ``/opt/pkcs11/usr/ssl/openssl.cnf``). 354 355See OpenSSL extension-specific documentation for instructions on configuring the PIN on 356the global level; doing so allows the ``dnssec-\*`` tools to access the HSM without 357PIN entry. (The ``pkcs11-\*`` tools access the HSM directly, not via OpenSSL, 358so a PIN is still required to use them.) 359