Windows Code Signing with eSigner

3 minute read.

I miss the good old days, when we still were able to use a pri­vate key for code sign­ing.

Nowa­days, in ad­di­tion to a high­ly ex­pen­sive cer­tifi­cate that re­quires val­i­da­tion of your com­pa­ny with “re­li­able third-par­ty web­sites” that may have a bunch of mis­in­for­ma­tion on you, we’re al­so ex­pect­ed to pay a dol­lar per API re­quest to sign a piece of code. Be­cause it re­quires spe­cial­ized hard­ware to host your pri­vate key or some­thing like that. I heard it might be al­most as ex­pen­sive as an Nvidia H100, or maybe not even close, yet some­how is way more ex­pen­sive to run a REST API re­quest on to jus­ti­fy the price… hm­mm.

Thanks to these new re­quire­ments, we now have to deal with a beau­ti­ful Ja­va-based tool that ships the JDK in­stead of the JRE and ends up be­ing a 200+ MB down­load (com­pressed!).

In­stal­la­tion

Ide­al­ly, just have it in­stalled on your run­ner once. If that’s not an op­tion, here is a script:

# Install eSigner CodeSignTool from SSL.com
Invoke-WebRequest -OutFile CodeSignTool.zip "https://www.ssl.com/download/codesigntool-for-windows/"
Expand-Archive -Force CodeSignTool.zip
Remove-Item CodeSignTool.zip
Move-Item -Destination "$Env:USERPROFILE\Desktop\CodeSignTool" -Path "CodeSignTool"

You can use it in your .git­lab-ci.yml di­rect­ly or save it as a in­stall-es­ign­er-tool.ps1 file and run it.

Code Sign from CMake

We code sign di­rect­ly from CMake, since we use CPack to pack­age up the signed ex­e­cuta­bles, and then run a POST_PACKAGE script to sign the in­stall­er.

This is our cus­tom tar­get to sign the ex­e­cutable:

set(CODESIGN_TOOL .\\CodeSignTool.bat)
set(CODESIGN_TOOL_DIR $ENV{USERPROFILE}\\Desktop\\CodeSignTool\\)
if(NOT EXISTS "${CODESIGN_TOOL_DIR}\\${CODESIGN_TOOL}")
    message(FATAL_ERROR "Missing ${CODESIGN_TOOL}")
endif()

# Windows code signing
add_custom_target(YourExecutableSigned
    COMMAND ${CODESIGN_TOOL} sign
        -credential_id="$ENV{CODESIGN_CREDENTIAL_ID}"
        -username="$ENV{CODESIGN_USER}"
        -password="$ENV{CODESIGN_PWD}"
        -input_file_path="$<SHELL_PATH:$<TARGET_FILE:YouExecutableTarget>>"
        -totp_secret="$ENV{CODESIGN_TOTP}"
        -override="true"
    WORKING_DIRECTORY ${CODESIGN_TOOL_DIR}
    COMMENT "Signing Editor executable")

Run it with cmake --build . --target YourExecutableSigned.

The WORKING_DIRECTORY is not op­tion­al. Our tool can on­ly run from in­side its own di­rec­to­ry.

Note that all the cre­den­tials are pro­vid­ed via en­vi­ron­ment vari­ables. Add them as CI se­crets:

  • CODESIGN_CREDENTIAL_ID: The eS­ign­er cre­den­tial ID from SSL.com.
  • CODESIGN_USER: Your SSL.com user­name.
  • CODESIGN_PWD: Your SSL.com pass­word.
  • CODESIGN_TOTP: The seed for the OTPs. This is re­quired, since you can’t in­put the time-based au­then­ti­ca­tor codes via CI, so you in­put the pri­vate to­ken in­stead, that the tool will use to gen­er­ate a OTP with.

You’ll have to re­place YouExecutableTarget with your tar­get, of course.

Hope this saved you some time.