OpenSSL with YubiHSM 2 via engine_pkcs11 and yubihsm_pkcs11

Install engine_pkcs11 and pkcs11-tool from OpenSC before proceeding. Depending on your operating system and configuration you may have to install [libp11](https://github.com/OpenSC/libp11/blob/master/INSTALL.md) as well. If you are on macOS you will have to [symlink pkg-config](https://gist.github.com/aklap/e885721ef15c8668ed0a1dd64d2ea1a7#gistcomment-2814899) in order to do so.

OpenSSL requires engine settings in the openssl.cnf file. Some OpenSSL commands allow specifying -conf ossl.conf and some do not. Setting the environment variable OPENSSL_CONF always works, but be aware that sometimes the default openssl.cnf contains entries that are needed by commands like openssl req.

In other words, you may have to add the engine entries to your default OpenSSL config file (openssl.cnf in the directory shown by openssl version -d) or add other requirements for your OpenSSL command into the config file.

It is suggested that you create a separate config file for interactions with the HSM in order to prevent conflicts with previous settings or defaults.

Example: Creating an Alias

An alias can be created to easily read from a dedicated config file and ensure compatibility across systems

alias yubissl='OPENSSL_CONF=/path/to/yubihsm.conf openssl'

Example: Generating a Key in the Device

Here is an example of generating a key in the device, creating a self-signed certificate and then signing a CSR with it:

$ pkcs11-tool --module /path/to/yubihsm_pkcs11.so --login --pin
   0001password --keypairgen --key-type rsa:2048 --label "my_key"
   --usage-sign
Using slot 0 with a present token (0x0)
Logging in to "YubiHSM".
Please enter User PIN:
Key pair generated:
Private Key Object; RSA
  label:      my_key
  ID:         04ec
  Usage:      sign
Public Key Object; RSA 2048 bits
  label:      my_key
  ID:         04ec
  Usage:      none

$ openssl req -new -x509 -days 365 -subj '/CN=my key/' -sha256 -config
    engine.conf -engine pkcs11 -keyform engine -key slot_0-label_my_key
    -out cert.pem
engine "pkcs11" set.
PKCS#11 token PIN:

$ OPENSSL_CONF=engine.conf openssl x509 -req -CAkeyform engine -engine
   pkcs11 -in req.csr -CA cert.pem -CAkey slot_0-label_my_key -set_serial
   1 -sha256
engine "pkcs11" set.
Signature ok
subject=/CN=test
Getting CA Private Key
PKCS#11 token PIN:
-----BEGIN CERTIFICATE-----
MIICkzCCAXsCAQEwDQYJKoZIhvcNAQELBQAwETEPMA0GA1UEAwwGbXkga2V5MB4X
DTE3MDQyNDA3Mzc1MFoXDTE3MDUyNDA3Mzc1MFowDjEMMAoGA1UEAwwDZm9vMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqBARJLAIjSqKk2OuRWrs91EC
MYjjZhxJE8IAMIiDDM2wSuQhB7A2CVW+/d1SG0k5cTEiasDBHbH9Bc2w+xn0l3Dh
8cXafvcFkjcNabHesrbcwRgItugw7PWBtyopWDtDhVWKS1zkpDO8iKjwiYciweaP
96nEHlQPPRUp7bf3IE7RTXENAqJai6QIYBZOrzHM9NrIz/6YaR2ua7SY7V/B3xaJ
7KsiQ8oHWuf+RDNkJOhbD+1fgeMtN8x+W4XYnCPQPjJ/MfjuHJ2n5EM3Vb/plh9H
uT+D56ozIk41FeXgC4gNu8fIv2KE1XBMuJCGRbyh5xk0dkQdvKxtVEfiDcwxBwID
AQABMA0GCSqGSIb3DQEBCwUAA4IBAQCHyskEU84T/YGhcjlpsdmobtyNhWc2ae/x
fmQpY/XGzQkSmUZJA+Z04JMUbli7UKEOItmqSlU6j0BPy03UjavNHdDPYcUZIS28
fPtzTkU3FdEBM/zkPXStBCo9+N34l4qSdir9hFWM1/CpkfP8PhteUQAqImXjbDVh
qhrfOg+kY3dAz91kLLXuA4YfuC+eEJh0JGuXCivhGre5LL9njrajHnJ+HSt6HHjC
R4U27/hzoK3r12XE5NjznjcaKk1AKFXZE92nqG/WYliyLpNNSrN+AmEKrPOHb8My
ZJlaGAfm3K9vLEjwrLQSAIKpMdpUcNE7Ay+EsEYTQpy43VvwI8vL
-----END CERTIFICATE-----

Example: Certifcate Request

For these examples, we assume you have all defaults and the engine config below in engine.conf, and provide an example of how to do the latter in the certificate request example below.

$ cat > engine.conf < <EOF
 openssl_conf = openssl_init

 [openssl_init]
 engines = engine_section

 [engine_section]
 pkcs11 = pkcs11_section

 [pkcs11_section]
 engine_id = pkcs11
 # dynamic_path is not required if you have installed
 # the appropriate pkcs11 engines to your openssl directory
 dynamic_path = /path/to/engine_pkcs11.{so|dylib}
 MODULE_PATH = /path/to/yubihsm_pkcs11.{so|dylib}
 # it is not recommended to use "debug" for production use
 INIT_ARGS = connector=http://127.0.0.1:12345 debug
 init = 0
EOF

$ OPENSSL_CONF=engine.conf openssl engine -t -c pkcs11
(pkcs11) pkcs11 engine
 [RSA, DSA, DH, RAND]
    [ available ]

Example: Retrieve 64 Bytes of Data

Here is an example of using the YubiHSM 2 PRNG via OpenSSL to retrieve 64 bytes of data:

$ OPENSSL_CONF=engine.conf openssl rand -engine pkcs11 -hex 64
engine "pkcs11" set.
2aae245fc6d1c0419684ee8968ce26fba2dc3bb48a91bae912c8a82b11db8186493
25800e6e984fedfa1940a24731dc2721431979a287252a214ebb87624dcf1

Example: Adding req entries

The following two examples will fail if you are only using the config above because it doesn’t have the req entries in openssl.cnf. You can integrate the engine.conf entries into the system’s openssl.cnf, or add the following to the end of the above engine.conf:

[ req ]
distinguished_name = req_dn
string_mask = utf8only
utf8 = yes

[ req_dn ]
commonName = Common Name (eg, your name)

Example: Requesting certificate existing RSA key

Here is an example of requesting a certificate for an existing RSA key with ID 3:

$ openssl req -new -subj '/CN=test/' -sha256 -config engine.conf
   -engine pkcs11 -keyform engine -key 0:0003
engine "pkcs11" set.
PKCS#11 token PIN:
-----BEGIN CERTIFICATE REQUEST-----
MIICVDCCATwCAQAwDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAJoTtK9p5XNDBaqy65IBDSj3mP9cpM0cw/sF/GZai6cx8Skf
DjAhqOkloN+Jdc2OsnaBVSqCbsSjVTXfc83oB2q4M3U/tl/nfzTGHGCA48dbKUiz
M807KoyYzFds9b7ZnGrwCmeXWjt2sAEGiJYEQt9gS9twabnCwxY4KySa9aNSNeHt
AwnfP5V60C73xA7ATOPjuWXq4TWgMWzRD0IwA3h7MIgtevJio2MTPWlspdGbYrxr
KsVfl/AocrSqYb44pMaRbAJAgOpJ8hsPjc9gkJnnrhmbkfV0v0AqjgwqxZa+BCWn
gdGl5HwKVFLu+X3lsBw7xHHJtOYgeFpp8twfvT0CAwEAAaAAMA0GCSqGSIb3DQEB
CwUAA4IBAQAcyImLuv7CrZJ1RPOf5d6u5LfYUadPXSGnozf3Ebgue12B51etKjYK
3cY8m9rRP3jRU5yWk3qoquZ7vCF7RNPf0N+7/blXHfoawx+ffEl/ToUZ5xr7IL0V
Qz9qzEumdNmm6MoQPxPOgrb1oCAz103gkf+S4HZGntO83/D31znsEhCSakoAa44s
3I+7vmzhjwUZsvMUg3sg2NCjRYRX2RPIPmtkDgufqsdAkNyWHlzitjfVMZxf8BcY
9DBrpQe1O6UbE1K9kYj2YBJ9h/FxfNJUk8t+rCcSOcQjmcRtgbHwhk2q77rapmg2
YliaYEU1/e5kl+v+0WEg7rvXgh/VkY2h
-----END CERTIFICATE REQUEST-----

Example: Self-Signed Certificate Existing RSA Key

Or alternatively a self-signed certificate for the same existing RSA key with ID 3:

$ openssl req -new -x509 -days 365 -subj '/CN=test/' -sha256 -config
    engine.conf -engine pkcs11 -keyform engine -key 0:0003
engine "pkcs11" set.
PKCS#11 token PIN:
-----BEGIN CERTIFICATE-----
MIICmjCCAYICCQDX5mJwg+YmMjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0
ZXN0MB4XDTE3MDMxNTIwMDkzOVoXDTE4MDMxNTIwMDkzOVowDzENMAsGA1UEAwwE
dGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJoTtK9p5XNDBaqy
65IBDSj3mP9cpM0cw/sF/GZai6cx8SkfDjAhqOkloN+Jdc2OsnaBVSqCbsSjVTXf
c83oB2q4M3U/tl/nfzTGHGCA48dbKUizM807KoyYzFds9b7ZnGrwCmeXWjt2sAEG
iJYEQt9gS9twabnCwxY4KySa9aNSNeHtAwnfP5V60C73xA7ATOPjuWXq4TWgMWzR
D0IwA3h7MIgtevJio2MTPWlspdGbYrxrKsVfl/AocrSqYb44pMaRbAJAgOpJ8hsP
jc9gkJnnrhmbkfV0v0AqjgwqxZa+BCWngdGl5HwKVFLu+X3lsBw7xHHJtOYgeFpp
8twfvT0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAHeSL6Qwqr8ST4SqnC1T2jjME
cjAT5eK4MqK3ayAy/Y/vYGtzARGIi9tGatyV6AFjs/0Me3/8du4bBVdC2DaP1hTf
m4m1HShHKFdUlwUGcwYoVNquCz8d6hDu3nL0XvtFKX77aHHQZeB3t0uD8evYZdTS
8oAduJpkAdJV7CtCLbGhLlLD3siYkd5fD35lhHlg8T2n5F4srDafQVdrDb/myYmI
2UmrZWvKDWZ3UvzKt1XVS8omIx7aTrUAPqv/SEdpPmJvg0pgWTKvzAtsnsxlRQdd
tdtJ/6nqhwXVSNXlDbyhFVo6J2u8BMEss2iausoSZBzf+YDOw2H+4GH6E11TmA==
-----END CERTIFICATE-----

Example: s_server with RSA Key and Certificate

Here is an example of using OpenSSL s_server with an RSA key and cert with ID 3.

By default this command listens on port 4433 for HTTPS connections.

$ env OPENSSL_CONF=engine.conf openssl s_server -engine pkcs11 -keyform
    engine -key 0:0003 -cert rsa.crt -www
engine "pkcs11" set.
PKCS#11 token PIN:
Using default temp DH parameters
ACCEPT
ACCEPT

Example: s_server with ECDSA Key and Certificate

Here is an example of using OpenSSL s_server with an ECDSA key and cert with ID 2:

$ env OPENSSL_CONF=engine.conf openssl s_server -engine pkcs11 -keyform
    engine -key 0:0002 -cert ecdsa.crt -www

Acknowledgements

We would like to thank Uri Blumenthal (uri@mit.edu) for contributing to this document.