This article describes how to set up a Smart Card/HSM backed OpenSSL CA using a Smart Card HSM or any PKCS11 enabled device.
Since some years back I use WPA2 Enterprise with EAP-TLS (Certificate authentication) for my wifi at home. Historically I have used certificates from a public CA for this purpose. This is not best practice since you don’t have control over the certificates that are issued.
Also, I recently bought a new switch capable of 802.1X authentication on all ports. For this purpose I want all my machines (even those without wifi) to have certificates. So I decided to go through the hassle of setting up my own private CA.
Setting up CA
For the basic setup of the CA I followed Jamies excellent guide on setting up a CA. So in this post you can assume that all the basic stuff like folders structure and basic commands are the same. I will only show you the differences needed to have the Root CA key stored on a PKCS11 device like a HSM, Smart Card HSM or a Yubikey. I will even try to follow his topic names so you can follow along.
Configure PKCS11 Engine
I will not discuss the operating system part of getting PKCS11 devices to work in this article. But basically you just need to install some packages, you can read about it here.
First of all we need to configure OpenSSL to talk to your PKCS11 device. This can be done from configuration or interactively on the command line.
# At beginning of conf (before everything else) openssl_conf = openssl_def # At end of conf (after everything else) [openssl_def] engines = engine_section [engine_section] pkcs11 = pkcs11_section [pkcs11_section] engine_id = pkcs11 dynamic_path = /usr/local/lib/engines/pkcs11.so MODULE_PATH = /usr/local/lib/opensc-pkcs11.so init = 0
OpenSSL> engine -t dynamic -pre SO_PATH:/usr/local/lib/engines/pkcs11.so -pre ID:pkcs11 -pre LIST_ADD:1 -pre LOAD -pre MODULE_PATH:/usr/local/lib/opensc- pkcs11.so
Create the root pair
First of all we need to have a RSA key pair on the PKCS11 device:
# pkcs11-tool --module /usr/local/lib/opensc-pkcs11.so -l --keypairgen --key-type rsa:2048 --label "SSL Root CA" Using slot 0 with a present token (0x0) Logging in to "HSM 2 (UserPIN)". Please enter User PIN: Key pair generated: Private Key Object; RSA label: SSL Root CA ID: d15c3e9578a612a658bb14e0e147db4f2279cf19 Usage: decrypt, sign, unwrap Public Key Object; RSA 2048 bits label: SSL Root CA ID: d15c3e9578a612a658bb14e0e147db4f2279cf19 Usage: encrypt, verify, wrap
Create the root certificate
I will assume that you have configured pkcs11 in openssl.cnf (otherwise you will have to first run the engine command in openssl interactively before any other command).
# openssl req -config openssl.cnf -new -x509 -days 7300 -sha256 -extensions v3_ca -engine pkcs11 -keyform engine -key 0:d15c3e9578a612a658bb14e0e147db4f2279cf19 -out certs/ca.cert.pem engine "pkcs11" set. Enter PKCS#11 token PIN for HSM 2 (UserPIN): 0x8018b6000 07:41:35.523 cannot lock memory, sensitive data may be paged to disk You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [SE]: State or Province Name : Locality Name : Organization Name [PeanOrg]: Organizational Unit Name :PeanOrg Certificate Authority Common Name :PeanOrg Root CA Email Address :
Create the intermediate pair
For the intermediate key pair I followed jamies guide. I need frequent access to this CA so I have decided to have the intermediate pair on file instead of HSM.
Create the intermediate certificate
I changed one thing in jamies intermediate/openssl.cnf because I dont see the point of having province set in the CAs
stateOrProvinceName = optional
To use the Root key stored on pkcs11 to sign the intermediate certificate use this command:
# openssl ca -config openssl.cnf -extensions v3_intermediate_ca -days 3650 -notext -md sha256 -engine pkcs11 -keyform engine -keyfile 0:d15c3e9578a612a658bb14e0e147db4f2279cf19 -in intermediate/csr/intermediate.csr.pem -out intermediate/certs/intermediate.cert.pem Using configuration from openssl.cnf engine "pkcs11" set. Enter PKCS#11 token PIN for HSM 2 (UserPIN): Check that the request matches the signature Signature ok Certificate Details: Serial Number: 4096 (0x1000) Validity Not Before: Apr 7 05:54:22 2018 GMT Not After : Apr 4 05:54:22 2028 GMT Subject: countryName = SE organizationName = PeanOrg organizationalUnitName = PeanOrg Certificate Authority commonName = PeanOrg Intermediate CA 1 X509v3 extensions: X509v3 Subject Key Identifier: 77:9C:07:23:FD:40:E9:5C:7E:30:73:8F:59:28:25:F5:06:43:B4:70 X509v3 Authority Key Identifier: keyid:A4:F2:DE:15:8E:9E:A8:87:B0:95:D4:21:A2:BD:4C:41:02:93:E0:8D X509v3 Basic Constraints: critical CA:TRUE, pathlen:0 X509v3 Key Usage: critical Digital Signature, Certificate Sign, CRL Sign Certificate is to be certified until Apr 4 05:54:22 2028 GMT (3650 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated
We now have all we need to sign certificates. Just follow Jamies guide Sign server and client certificates
It took me a few hours to get this going because of sort of a lack of documentation on how to use OpenSSL and PKCS11 together, during my efforts I found these resources helpful