This might save someone a few hours working out the steps to set up and use a YubiHSM for code signing. My use case was low-volume software signing; the useful part was getting the Windows certificate tools to use a key on the device.

Summary

  1. Plug in the YubiHSM
  2. Install the YubiHSM software
  3. Use the template provided by YubiCo to produce an inf file.
  4. Create a CSR using the .inf as input.
  5. Take the CSR to a Certificate Authority and generate a new certificate
  6. Install the issued certificate in the certificate store on the signing machine
  7. Sign things.

The Notes

For Step 1 and 2: Plug in the YubiHSM, Install the YubiHSM software

You’ll need the SDK package for your operating system. The 2019 Windows package used in this lab is historical; use Yubico’s releases for a new installation.

Once you’ve done that install both the CNG and the Connector, they are contained in the zip.

Before putting a real signing key on it, configure the HSM authentication key and tell the KSP which key ID to use. The factory authentication key is only a starting point. Yubico’s Windows setup guide covers the KSP configuration and required capabilities. A healthy connector status page doesn’t prove those credentials or capabilities are correct.

You can test that things are looking rosy by navigating to the directory that you unpacked the YubiHSM SDK and navigating to the bin folder. There’s a tool in there called yubihsm-shell.exe that will allow you to make your first interaction with the device.

yubihsmshell

You’ll also notice that a status page for the HSM connector is available at http://localhost:12345/connector/status on the machine.

yubistatus

For Step 3: Use the template provided by YubiCo to produce an inf file describing the signing certificate you need.

Yubico’s current code-signing example includes the request template.

Chances are, you don’t need to modify much in the template. For me, just the Subject. But check with your Certification Authority folks if you believe you have some additional things in your CSR.

[Version]
Signature="$Windows NT$"

[NewRequest]
Subject = "CN=DropbearSec" ; Entity name (dns name/upn for other cert types)
HashAlgorithm = sha256
KeyAlgorithm = RSA
Exportable = FALSE ; Private key is not exportable
KeyLength = 2048 ; YubiHSM KSP key sizes: 2048, 3072, 4096
KeySpec = 2 ; 1 = AT_KEYEXCHANGE, 2 = AT_SIGNATURE
KeyUsage = 0x80 ; 80 = Digital Signature, 20 = Key Encipherment (bitmask)
MachineKeySet = False ; True: cert belongs the local computer, False: current user
ProviderName = "YubiHSM Key Storage Provider"
ProviderType = 1
SMIME = FALSE
RequestType = PKCS10 ; Confirm the request format with your CA

[Strings]
szOID_ENHANCED_KEY_USAGE = "2.5.29.37"
szOID_CODE_SIGN = "1.3.6.1.5.5.7.3.3"
szOID_BASIC_CONSTRAINTS = "2.5.29.19"

[Extensions]
%szOID_ENHANCED_KEY_USAGE% = "{text}%szOID_CODE_SIGN%"
%szOID_BASIC_CONSTRAINTS% = "{text}ca=0&pathlength=0"

; If you are using ADCS with certificate templates, you may add
; a specific template under [RequestAttributes]
;[RequestAttributes]
;CertificateTemplate= CodeSigning

The important part in that request template is the ProviderName = "YubiHSM Key Storage Provider". In the next step the certificate request will be generated using that CNG provider (saying, please refer to the YubiHSM for the private key material)

For Step 4: Create a CSR using the .inf as input.

You can use:

certreq -new sign.inf sign.req

You should see:

CertReq: Request Created

You can also look on the YubiHSM and notice that you have a new object:

yubiobects

Before submitting, decode the request so we can check the subject, key size and requested extensions. Printing the base64 request doesn’t tell us much:

certutil -dump sign.req

The CA decides what goes in the issued certificate, so check that as well when it comes back.

For Step 5: Take the CSR to a Certificate Authority and generate a new certificate

This is where you submit the CSR to your certificate authority. For a publicly trusted code-signing certificate, check the CA’s hardware and enrollment requirements before generating the key; a CSR on its own doesn’t demonstrate how the private key is protected.

(It is also possible that your CA is an internal corporate one; this will also apply to you, the difference is that you will log onto the CA and issue a certificate yourself using the CSR as input).

Download the certificate when it is ready.

For Step 6: Install the issued certificate in the certificate store on the signing machine

On the machine and under the user that created the request, accept the issued certificate:

certreq -accept -user sign.cer

This completes the pending request and links the certificate to its private key. Our template uses MachineKeySet = False, so we’re using the current user’s certificate store. A machine-store setup needs that choice made consistently at request creation, acceptance and signing; copying a certificate between stores isn’t enough. Certreq reference.

The CNG provider that we installed at the start is the thing that is going to make it possible for this certificate to be linked to the private key material on the YubiHSM when we are signing code.

For Step 7: Sign things

Try it out:

You’ll need the Microsoft code signing tools already installed.

You can find them in the Windows 10 SDK. There’s more information about signtool here

signtool sign /v /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 /a /d "DropBearSec Signing Machine" /as c:\tmp\testfile.exe

No /sm here: that would make SignTool search the machine store, while this request created a user key. SHA-256 is used for both the file digest and timestamp digest. SignTool options.

if you hit issues you can add a debug parameter to show you the certificates that were considered:

signtool sign /v /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 /a /debug /d "DropBearSec" /as c:\tmp\test.exe

Then check the result, including the timestamp:

signtool verify /pa /all /v /tw c:\tmp\testfile.exe

If more than one certificate matches, use /sha1 <certificate-thumbprint> to select the intended one. Despite the option’s name, that is a certificate identifier; /fd SHA256 still controls the file’s signing digest.

References