Code signing a VSIX package with a hardware-based USB token key

Yes, I know that 5 years have passed since my last post here. That last post was about Code signing a VSIX package targeting multiple Visual Studio versions and this one is a continuation. As you may know, there are three kinds of code signing certificates:

  • Individual Validation (IV)
  • Organization Validation (OV)
  • Extended Validation (EV)

Until June 1, 2023, only EV certificates were issued on hardware-based devices such as USB keys. But since that date, IV and OV certificates provided by all vendors are only issued either on Federal Information Processing Standard 140-2 (FIPS 140-2) USB tokens or some kind of cloud-based code signing FIPS-compliant service. This change is in compliance with the Certificate Authority/Browser (CA/B) Forum’s new key storage requirements to increase security for code signing keys. Although it is expected that instances of code signing keys being stolen and misused by malicious actors will be greatly reduced, this poses a challenge to sign VSIX packages using the vsixsigntool.exe tool due to lack of documentation.

In this scenario, you have:

  • A certificate without the private key. You have got this certificate from the vendor and imported into the USB key, or you have got a USB device with the certificate already preloaded from the vendor directly. When you insert the key in the USB port (with maybe a Windows smart card driver installed) the certificate appears also in the Windows Certificate Store for user (certmgr.exe), in the Personal > Certificates node. Apparently the vsixsigntool.exe tool doesn’t support certificate files in .per or .crt formats, and the .pfx format requires the private key, so you have to use the .p7b format for the certificate file. You can export the certificate from the Windows Certificate Store to the .p7b format.
  • A private key inside the USB key that cannot leave the device (hence the enhanced security).

Since you don’t have a password-protected .pfx file with the certificate and the private key, you can’t use the /p flag (for the password) of the vsixsigntool.exe tool. Instead, you must provide access to the private key inside the USB device using the following flags:

  • /f: Path to the certificate file in .p7b format (without the private key)
  • /csp : Specify the Cryptographic Service Provider (CSP) containing the Key Container of the private key.
  • /k : Specify the name of the Key Container of the private key.

The steps to sign VXIS packages with a hardware-based USB key are the following:

  • Get the SHA1 Thumbprint of the certificate. To get it, locate the certificate Windows certificate store of user (certmgr), open it and in the Details tab look for the Thumbprint property. It should be a string of 40 characters.
  • Get the name of the CSP provider containing the container of the private key. To get it use this command and use the “Provider” field of the output:

    certutil.exe -user -store my "<SHA1-thumbprint-of-your-certificate>"

    It should be something like “Microsoft Smart Card Key Storage Provider”.
  • Get the name of the Key Container of the private key. To get it use the same previous command and use the “Key Container” field of the output. It should be something like a GUID.

With all those pieces, the command would be something like this:

vsixsigntool.exe sign /f <CodeSigningCertificate.p7b> /csp <CertificateCryptographicServiceProvider> /k <CertificateKeyContainerName> /sha1 <CertificateSHA1Thumbprint> /fd sha256 /t <TimeStampServerUrl> <MyVSIXProject.vsix>

At that point you should get a prompt to enter the PIN of your USB key to get access to the private key and the VSIX file should be signed successfully.