Automate Extended Validation (EV) code signing with Yubico Yubikey

684 Views Asked by At

I want to EV Code sign using a private key on a Yubico Yubikey 5 using signtool.exe, However everytime I sign a file, it prompts for the password. I would like the entry of this key to be automated as part of a build pipeline triggered on a secure machine.

I previously used a Safenet token and was able to do this using solutions from this question, however my understanding is that this functionality was a specific implementation for the eToken Cryptographic Security Provider used by Safenet.

I have exported the public certificate from the Yubikey and used certutil to dump info about the key container (which is a GUID like 0bababab-0000-aaaa-baba-cdcdcdcdcdcd) and the CSP which is Microsoft Smart Card Key Storage Provider but when I use this info I get the following issue:

signtool sign /f <cert file>.crt /csp "Microsoft Smart Card Key Storage Provider" /v /kc <container_id>
Error information: "CryptExportPublicKeyInfoEx failed" (-2146893819/0x80090005)
SignTool Error: An unexpected internal error has occurred.
3

There are 3 best solutions below

0
Naxin On BEST ANSWER

In addition to Microsoft Smart Card support the Yubikey also supports PKCS11. This interface isn't natively supported by signtool.exe but it is supported by third party tools like osslsigncode which allows the key password to be supplied over the CLI.

Here's how I got it working:

  1. Download and install OpenSSL i.e. from here Important: make sure to install it to C:\OpenSSL-Win64 instead of the default of C\Program Files\OpenSSL-Win64
  2. Download and install the yubico-piv-tool i.e. from here and note the location of the file libykcs11.dll which should be at C:\Program Files\Yubico\Yubico PIV Tool\bin Important: Add this path to your PATH variable or osslsigncode won't work.
  3. Clone or download the source for libp11
  4. Compile libp11 using nmake, more detailed instructions here. Note: The current version of the make file is sensitive to OpenSSL being installed to the directory in Step 1
  5. Note the location of pkcs11.dll which is produced from Step 4 this will be our pkcs11engine for osslsigncode
  6. Download osslsigncode i.e. from here and add its location to your PATH variable
  7. Use Yubikey Manager to export the public certificate to a .crt file and note its location
  8. Finally you can use the following command:
    osslsigncode sign -pkcs11module <location_of_libykcs11.dll> -pkcs11engine <location_of_pkcs11.dll> -pass <yubikey_passcode> -ts <timestamp_server_url> -key "pkcs11:id=%01" -certs <path_to_public_cert.crt> -in "input_file.exe" -out "output_file.exe"

Additional resources:

4
wqw On

Here is my s.bat based on osslsigncode.exe, libykcs11.dll and pkcs11.dll (x64 release)

Usage: s.bat myproject.exe

@echo off
setlocal

if "%1"=="" echo usage: %~nx0 ^<myproject.exe^> & goto :eof

call :codesign "%~1" ^
    "D:\TEMP\osslsigncode-2.7-windows-x64-vs\bin\osslsigncode.exe" ^
    "C:\Program Files\Yubico\Yubico PIV Tool\bin\libykcs11.dll" ^
    "C:\Work\Temp\libp11\src\pkcs11.dll" ^
    "C:\Work\Certificates\SSL.com\unicont soft ltd.chained_1.crt" ^
    "http://ts.ssl.com" ^
    %_UCS_YUBIKEY_PIN%
goto :eof

:codesign
set path=%~dp2;%~dp3;%path%

echo SHA-256 %~dpnx1 -^> %~n1_signed%~x1
"%~2" -in "%~dpnx1" -out "%~dpn1_signed%~x1" ^
    -pkcs11module "%~3" -pkcs11engine "%~4" ^
    -certs "%~5" -key "pkcs11:id=%%01" -pass "%~7" ^
    -h sha256 -ts "%~6" -nolegacy  || goto :eof    
move /y "%~dpn1_signed%~x1" "%~dpnx1" > nul
goto :eof

:codesign_legacy
set path=%~dp2;%~dp3;%path%

echo SHA-1 %~dpnx1 -^> %~n1_signed%~x1
"%~2" -in "%~dpnx1" -out "%~dpn1_signed%~x1" ^
    -pkcs11module "%~3" -pkcs11engine "%~4" ^
    -certs "%~5" -key "pkcs11:id=%%01" -pass "%~7" ^
    -h sha1 -ts "%~6" -nolegacy  || goto :eof    
echo SHA-256 %~n1_signed%~x1 -^> %~dpnx1
"%~2" -in "%~dpn1_signed%~x1" -out "%~dpnx1" ^
    -pkcs11module "%~3" -pkcs11engine "%~4" ^
    -certs "%~5" -key "pkcs11:id=%%01" -pass "%~7" ^
    -h sha256 -ts "%~6" -nolegacy -nest || goto :eof
del /q "%~dpn1_signed%~x1"
goto :eof

The Yubikey PIN should be set in _UCS_YUBIKEY_PIN environment variable or edit the source.

Unfortunately SSL.com issues only ECDSA code-signing certificates which will never work on legacy Windows XP SP3 systems so my attempts above were futile.

Edit: Singing Web Service

Here is a cheap web service (in golang) which shells helper s.bat above and can be used to sign your build binaries remotely:

Make sure to run the service executable in Console session on server, i.e. use Task Scheduler to run exe at startup (in proper folder too).

3
Emmanuel Bourg On

You can use Jsign instead of signtool (disclaimer: I'm the author), it supports Yubikeys natively and doesn't require any PKCS#11 library to be installed.

The syntax looks like this:

jsign --storetype PIV --storepass 123456 --alias SIGNATURE \
      --certfile full-chain.pem application.exe