This example will show you how to create a code-signing certificate request using a key generated and stored in the YubiHSM 2 via the Key Storage Provider (KSP). This type of code-signing certificate is appropriate for use with the Microsoft signtool
utility for digitally signing Windows binaries.
In this example, we will use the command line certreq
utility. All procedures documented here are available in the Certificate Manager (certmgr.msc
) MMC snap-in if you prefer a UI experience.
The KSP will by default use the factory authentication key in slot 1. If the factory authentication key no longer exists or use of a different authentication key is desired, the KSP must first be configured with the desired key ID and password.
Note that the configured authentication key must at a minimum have capabilities generate-asymmetric-key,sign-pkcs
and delegated capability sign-pkcs
. If you want the generated key to be exportable, then add the exportable-under-wrap
delegated capability.
Create a new Authentication Key capable of generating exportable asymmetric keys through KSP.
yubihsm> put authkey 0 0 "GenerateKey" 1 generate-asymmetric-key,sign-pkcs sign-pkcs,exportable-under-wrap password Stored Authentication key 0x0e32
The certreq
utility requires a .inf file as input which defines your request. An example file is supplied here:
sign.inf
[Version] Signature="$Windows NT$" [NewRequest] Subject = "CN=My Publisher" ; Entity name (dns name/upn for other cert types) HashAlgorithm = sha256 ; Request uses sha256 hash KeyAlgorithm = RSA ; Key pair generated using RSA algorithm Exportable = FALSE ; Private key is not exportable ExportableEncrypted = FALSE ; Private key is not exportable encrypted 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 KeyUsageProperty = NCRYPT_ALLOW_SIGNING_FLAG ; Private key only used for signing, not decryption UseExistingKeySet = FALSE ; Do not use an existing key pair ProviderName = "YubiHSM Key Storage Provider" ProviderType = 1 SMIME = FALSE ; No secure email function UseExistingKeySet = FALSE ; Do not use an existing key pair RequestType = PKCS10 ; Can be CMC, PKCS10, PKCS7 or Cert (self-signed) [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
Once you’ve created the certificate request configuration file, pass it to certreq
as the input file argument, e.g:
certreq -new sign.inf sign.req
In the above example, the certificate request was written to sign.req. Take this file and submit its contents to your CA for signature. Once signed, open the resulting file (e.g., sign.crt) and install the certificate to your personal store.
Open a prompt with signtool
in the path and use the following command to sign your binary.
> signtool sign <binary name>
If you have multiple certificates available for code signing, it may be necessary to identify your signing certificate by hash. If this occurs, signtool
will show you a list of valid certificates. Simply re-run sign tool with the sha1 hash of the certificate:
> signtool sign /sha1 <certificate hash> <binary name>
When importing the certificate for the first time on a new computer, it may be necessary to manually bind the certificate to the private key. This is because the key is not stored with the certificate and Windows doesn’t automatically create an association between the two.
After importing the certificate to your personal store, use the certutil
utility
provided by Windows to associate the YubiHSM private key to the certificate.
> certutil -repairstore my <certificate hash>
The error messages returned from signtool
are often unhelpful in diagnosing why
a signing operation failed.
In these situations there are a few commands you can use to try to track down the root cause.
When using signtool
, use the /v
and /debug
flags to get more detailed output.
The example below shows a response you may receive if the certificate is installed
but the YubiHSM is not connected or is misconfigured.
> signtool sign /v /debug <binary name> After EKU filter, 1 certs were left. After expiry filter, 1 certs were left. After Hash filter, 1 certs were left. After Private Key filter, 0 certs were left. SignTool Error: No certificates were found that met all the given criteria.
Use certutil
to check the validity of the imported certificate.
> certutil -verifystore my <certificate hash> ================ Certificate 0 ================ Serial Number: 029fe48291dd587c1e6f42bca341291 ... Certificate is valid
Use certutil
to check whether the KSP has been installed correctly.
You should see Provider Name: YubiHSM Key Storage Provider
as one of the entries with no errors.
> certutil -csplist ... Provider Name: YubiHSM Key Storage Provider ...
Use certutil
to check if the key is accessible through the storage provider.
You can also add the -v
flag to get additional details.
> certutil -csp "YubiHSM Key Storage Provider" -key YubiHSM Key Storage Provider: tq-75c94c4b-5e40-4e44-bcd2-ee3330d4942f RSA AT_SIGNATURE
Use certutil
to dump certificate information.
This command may show Cannot find the certificate and private key for decryption.
when using a new computer if certutil -repairstore
hasn’t yet been performed.
> certutil -store my <certificate hash> ================ Certificate 0 ================ Serial Number: 029fe48291dd587c1e6f42bca341291 ... Private key is NOT exportable Signature test passed
For a detailed explanation of all options available in the request inf file, please see the documentation for the certreq utility.
To generate a similar request using the Certificate Manager, open the Certificate Manager snap-in, select the Personal/Certificates store, right click and select All Tasks→Advanced Operations→Create Custom Request.