Using Key Storage Provider (KSP) – Windows Only

If the target private key is managed by the Microsoft Software Key Storage Provider, another software provider, or any other KSP that allows export via PKCS#12 PFX, it is possible to move your key to the YubiHSM 2, but results may vary.

This process relies on using the -repairstore functionality of the certutil command, so the private key must only be present via the YubiHSM Key Storage Provider when performing this step. Please refer to the source storage provider documentation for how to cleanly and completely delete a private key.

Because KSP implementations differ, we recommend testing this procedure using your existing provider before affecting a live system.

Export your Existing Private Key and Certificate

Refer to your current KSP documentation on how to obtain a PKCS#12 PFX export of your certificate and private key.

Step 1:

Obtain your PFX file.

Step 2:

Split the certificate from the PFX file using certutil.

PS1> certutil -split -dump <pfx file>

This creates a file named <Cert Hash>.crt.

Step 3:

If you are moving the key to the YubiHSM 2 on the same machine, you must delete the original private key in your current provider.

PS1> certutil -key
Step 4:

Locate the key that corresponds with the CA. It may look something like this:

Microsoft Software Key Storage Provider:

EXAMPLE-CA abcdef1234fedcba4321abcdef123456_9cfc1053-1b5a-44d7-
8a7e-3a8a1c0d0db0 RSA AT_KEYEXCHANGE
Step 5:

To delete this example private key.

PS1> certutil -delkey -csp "Microsoft Software Key Storage Provider"
     "abcdef1234fedcba4321abcdef123456_9cfc1053-1b5a-44d7-8a7e-3
     a8a1c0d0db0"

Import the Target Private Key

Using the instructions for importing a PFX private key, see PUT ASYMMETRIC KEY Command via yubihsm-shell, import the target private key file to your YubiHSM 2.

Step 1:

Record the Label property of your imported key.

Important

The certutil utility does not provide an easy way to split a key exported from the Software KSP into an unencrypted PEM file. It may be necessary to use another tool like OpenSSL to convert the key file to an unencrypted format for import into the HSM.

Step 2:

Export the private key.

PS1> openssl pkcs12 -in <pfx file> -nocerts -out ca.key -nodes
Step 3:

To remove the passphrase from the private key.

PS1> openssl rsa -in ca.key -out ca.key

Restore the Target Certificate

Step 1:

Move the target certificate file (<Cert Hash>.crt) to the target machine.

Step 2:

Import the certificate to the LocalMachine “My” store via your favorite method.

At this point, the certificate does not have an associated private key. We use the -repairstore functionality of certutil to re-associate the certificate to the private key.

Step 3:

Make sure that the target private key is visible via the YubiHSM KSP.

PS1> certutil -key -csp "YubiHSM Key Storage Provider"

This command lists all private keys visible to the current Authentication Key. It also lists the private keys corresponding container names - which are equal to the Label property in the YubiHSM 2.

Step 4:

Open an elevated prompt and execute the command.

PS1> certutil -repairstore MY <Cert Hash>
Step 5:

Verify that the certificate has been associated with the YubiHSM KSP and has the correct Key Container property value.

PS1> certutil -store My
Step 6:

Inspect the Key Container and Provider properties.

Warning

If you are moving your CA key to the YubiHSM 2 on the same machine, Windows Certificate Services (CertSvc) on the local machine writes the name of the KSP to its configuration section in the registry. When signing requests, the certificate service will fail if the KSP name does not match the name in the registry.

Step 7:

Update the KSP name for the local certificate service.

  • Open an elevated prompt and execute the commands.

    PS1> certutil -setreg CA\CSP\Provider "YubiHSM Key Storage Provider"
    PS1> certutil -setreg CA\EncryptionCSP\Provider "YubiHSM Key Storage
         Provider"
    
  • Optionally, if you have multiple CAs on the same machine, or prefer to edit the registry directly. These settings are located at:

    HKLM\System\CurrentControlSet\Services\CertSVC\Configuration\<CAName>\[CSP | EncryptionCSP]

Status Codes Reference

The YubiHSM software components have a standard set of status codes to report the status of an HSM operation. To comply with the expectations of specific platforms, these status codes are converted to the appropriate API status code.

Currently, this translation is only performed for the Windows Key Storage Provider. The error codes, their meanings and translated values are as follows.

Libyubihsm Error Code Description Windows CNG Translation
YHR_BUFFER_TOO_SMALL
Not enough space
to store data
NTE_BUFFER_TOO_SMALL
YHR_CONNECTION_ERROR
Transport Backend
error
NTE_DEVICE_NOT_READY
YHR_CONNECTOR_ERROR
Connector
operation Failed
NTE_DEVICE_NOT_READY
YHR_CONNECTOR_NOT_FOUND
Unable to find a
suitable connector
NTE_DEVICE_NOT_READY
YHR_CRYPTOGRAM_MISMATCH
Unable to verify
cryptogram
NTE_BAD_SIGNATURE
YHR_DEVICE_AUTHENTICATION
_FAILED
Message encryption /
verification failed
NTE_INCORRECT_PASSWORD

YHR_DEVICE_COMMAND
_UNEXECUTED







The HSM attempted to
execute a command,
but it did not
complete in allotted
time. The command
has not terminated,
and the current
state of the session
is unavailable
NTE_SYS_ERR
YHR_DEVICE_DEMO_MODE

Demo mode, power
cycle device
NTE_DEVICE_NOT_READY
YHR_DEVICE_INSUFFICIENT
_PERMISSIONS
Wrong permissions
for operation
NTE_PERM
YHR_DEVICE_INVALID
_COMMAND
Invalid command NTE_NOT_SUPPORTED
YHR_DEVICE_INVALID_DATA
Malformed command /
invalid data
NTE_INVALID_PARAMETER
YHR_DEVICE_INVALID_ID
Illegal ID used
NTE_INVALID_PARAMETER[]
YHR_DEVICE_INVALID_OTP
Invalid OTP
NTE_INCORRECT_PASSWORD
YHR_DEVICE_INVALID
_SESSION
Invalid session NTE_DEVICE_NOT_READY
YHR_DEVICE_LOG_FULL


Log buffer is full
and forced audit is
set
NTE_DEVICE_NOT_READY
YHR_DEVICE_OBJECT_EXISTS
An object with the
specified ID already
exists
NTE_EXISTS
YHR_DEVICE_OBJECT
_NOT_FOUND
Object not found NTE_NOT_FOUND
YHR_DEVICE_OK No error NTE_OP_OK
YHR_DEVICE_SESSION_FAILED
Session creation
failed
NTE_DEVICE_NOT_READY
YHR_DEVICE_SESSIONS_FULL
All sessions are
allocated
NTE_DEVICE_NOT_READY
YHR_DEVICE_STORAGE_FAILED Storage failure
NTE_TOKEN_KEYSET
_STORAGE_FULL
YHR_DEVICE_WRONG_LENGTH Wrong length NTE_BAD_LEN
YHR_GENERIC_ERROR Generic error NTE_FAIL
YHR_INIT_ERROR
Unable to initialize
libyubihsm
NTE_PROVIDER_DLL_FAIL
YHR_INVALID_PARAMETERS
Invalid argument to
a function
NTE_INVALID_PARAMETER
YHR_MAC_MISMATCH
Unable to verify MAC
NTE_BAD_SIGNATURE
YHR_MEMORY_ERROR
The YubiHSM or
software library was
not able to allocate
memory to perform
the requested
operation
NTE_NO_MEMORY
YHR_SESSION
_AUTHENTICATION_FAILED
Unable to
authenticate session
NTE_INCORRECT_PASSWORD
YHR_SUCCESS
The operation
completed
Successfully
ERROR_SUCCESS
YHR_WRONG_LENGTH
This error may occur
if there is a
mismatch between the
YubiHSM firmware
version and
libyubihsm library
version
NTE_BAD_LEN

Example: Creating a Code-Signing Certificate using the Key Storage Provider

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 use the command line certreq utility. All procedures documented here are available in the Certificate Manager (certmgr.msc) MMC snap-in if you prefer to use a GUI.

Note

For operations that take input data (from command line or file), releases prior to and including the current yubihsm2-sdk release have a size limit - 4kb in interactive mode, or 8kb in non-interactive mode.

Configure the Key Storage Provider

By default, the KSP will use the factory authentication key in slot 1. If the factory authentication key no longer exists or a different authentication key is desired, the KSP must first be configured with the desired key ID and password.

Note

The configured authentication key must at a minimum have the 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.

Authentication Key Example

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

Create the Certificate Request Configuration File

To specify your request, the certreq utility requires an .inf file as input. An example file is supplied here.

Sample 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

Create the Certificate Request

Once you have created the certificate request configuration file, pass it to certreq as the input file argument. For example:

certreq -new sign.inf sign.req

Sign the Certificate Request

In the above example, the certificate request was written to sign.req.

Step 1:Take this file and submit its contents to your CA for signature.
Step 2:Open the resulting file (for example, sign.crt) and install the certificate to your personal store.

Sign using Signtool

Step 1:

Open a prompt with signtool in the path.

Step 2:

Sign your binary.

> signtool sign <binary name>
Step 3:

Identify your signing certificate by hash, if you have multiple certificates available for code signing.

signtool shows you a list of valid certificates. Re-run sign tool with the sha1 hash of the certificate:

> signtool sign /sha1 <certificate hash> <binary name>
Step 4:

Associate the YubiHSM private key to the certificate.

When importing the certificate for the first time on a new computer, you need to manually bind the certificate to the private key. This is needed because 1) the key is not stored with the certificate and 2) Windows doesn’t automatically create an association between the private key and the certificate.

After you import the certificate to your personal store, use the certutil utility provided by Windows.

> certutil -repairstore my <certificate hash>

Troubleshooting

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 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 might 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.

If the command shows Cannot find the certificate and private key for decryption. when using a new computer, it might indicate that 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, see the documentation for the certreq utility.

To generate a similar request using the Certificate Manager:

  1. Open the Certificate Manager snap-in.
  2. Select the Personal/Certificates store.
  3. Right click and select All Tasks > Advanced Operations > Create Custom Request.