My 2021-2024 Authenticode certificate expired yesterday, so I began the process of getting a replacement last week. As in past years, I again selected a 3 year OV certificate from DigiCert.
Validation was straightforward. After placing my order, I got a request for high-resolution photos of me holding my ID (I sent my passport and driver’s license). Then, a verification Zoom video call was scheduled (they had tons of slots open, I did mine when I was free at 10:30PM) where I showed the validator my ID and signed the attestation forms with them acting as a witness. I scanned the completed forms to a PDF and emailed it to the validator.
In 2023, the Baseline Requirements for code-signing were updated to require that all code-signing certificates be stored on hardware to limit theft and abuse. I’ve been storing my code-signing certificates on a hardware token since 2015 or so, and using a YubiKey 4 since 2016. However, Digicert now seems to require a new token, either a SafeNet eToken or a Common Criteria EAL4+ standard or FIPS 140-2 level 2 HSM. As far as I know, my YubiKey4 doesn’t qualify, so I ticked the option to have them send me a new SafeNet eToken 5110 CC for $120. (There are probably places to get this less expensively, but I didn’t want to fuss over it.)
A few days later, my new token arrived:
I popped it in my PC, installed the two Windows apps that DigiCert directed me to install, generated my new certificate, and set a new PIN. The process took perhaps fifteen minutes.
I uninstalled my expiring certificate from my Windows certificate manager (since it has the same Subject Name as the new one) so I would not need to make any changes to my build and sign batch script:
@title FiddlerImportNetLog Builder
@cd C:\src\FiddlerImportNetLog\installer
@C:\tools\signtool sign /as /d "Fiddler NetLog Importer" /du "https://textslashplain.com/" /n "Eric Lawrence" /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 C:\src\FiddlerImportNetLog\FiddlerImportNetLog\bin\release\FiddlerImportNetLog.dll
@filever C:\src\FiddlerImportNetLog\FiddlerImportNetLog\bin\release\FiddlerImportNetLog.dll > Addon.ver
@C:\src\NSIS\MakeNSIS.EXE /V3 FiddlerImportNetLog.nsi
@CHOICE /M "Would you like to sign?"
@if %ERRORLEVEL%==2 goto done
:sign
@C:\tools\signtool sign /as /d "Fiddler NetLog Importer" /du "https://textslashplain.com/" /n "Eric Lawrence" /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 FiddlerImportNetLog.exe
@if %ERRORLEVEL%==-1 goto sign
@:done
@title Command Prompt
I took the opportunity to stop adding an additional signature that uses SHA1, since there’s no reason why you’d want to run this app on Windows XP (which didn’t support SHA256).
In past years, it was common to simply sign your application’s installer, main executable, and anything that required elevation to run as Administrator — the three contexts in which signatures were most commonly checked by various components of the system (e.g. the Shell, firewalls/security software, and the UAC Elevation dialog). For the most part, other signatures were not automatically checked by the system (the fact that AV software is constantly looking at file signatures was basically invisible).
Things have changed with the start of the rollout of Windows 11’s Smart App Control feature.
Rather than just checking the signature of your installer, a system with SAC enabled will now check the signature on every executable module your program loads. If a signature is not found, an reputation check is conducted against the Defender File Metadata Service. If a positive reputation isn’t found, the module load is blocked.
You can see this at work when installing an old utility app I built, MezerTools. This product contained one unsigned DLL I knew about — docker.dll, a DLL that implements a feature to allow users to double-click any window’s edge to “grow” it to the corresponding edge of the screen. Because this DLL is unsigned, it fails to load, and the Bad Image
notification dialog is shown.
In the blocking dialog, two different Error Status codes are common:
Beyond that expected failure for docker.dll
, there’s another one I didn’t anticipate, visible only in the notification toast shown by Windows complaining about System.dll
. My app doesn’t install a System.dll, but the Nullsoft Install System I use to install MezerTools will try to drop its own System.dll
to a temporary folder during install if your installation script uses certain NSIS functions (in my case, computing a folder’s size). Because the NSIS DLL isn’t signed, it is blocked and my API calls in the script will fail.
There’s a third failure not seen until the user tries to uninstall the app. The Uninst.exe
uninstaller that Nullsoft automatically generates isn’t signed and can be blocked by SAC:
The install script compiler writes its uninstaller directly into the built Setup.exe
, making it easy for the developer to forget about. Even if they remembers the uninstaller, how can they sign it?
Fortunately, the developers of NSIS have already solved that one by introducing the !uninstfinalize
command. This command enables the developer to sign the uninstaller generated by NSIS before it is embedded into the installer.
Beyond getting a certificate from a third-party CA, Microsoft now offers its Azure Trusted Signing service in Public Preview. With Azure Trusted Signing, your private key is stored securely in a security module in the cloud and used to sign your binaries. You can integrate your local signing processes with Trusted Signing and integrate signing into cloud build processes on GitHub and the like.
This is a cool approach and widely considered to be “the future,” but it’s overkill for my needs, and I do all of my builds on my local PC. I like the idea that anything bearing my digital signature is something that I literally/virtually had my hands on (by plugging my token into my PC).
Beyond that, Azure Trusted Signing is not presently available for individuals or legal entities younger than three years old. They do expect to relax that at some point in the future, so check back here for an update when I next need a certificate in 2027. :)
For most scenarios, any certificate that chains to a trusted root in the Microsoft Root Certificate Program will be acceptable for Windows scenarios. There are a few special exceptions however.
If you wish to sign a driver or other Windows component, you must use an Extended Validation Certificate. EV certificates require additional vetting as to the identity of the publisher and are not generally available to individuals. Notably, as of 2019, using an EV certificate no longer grants your code “initial trust” by SmartScreen Application Reputation.
Windows 11 allows 3rd party developers to build code that runs inside a Secure Enclave (I’ve written about Enclaves recently). In order to load code within an enclave, the signing certificate must have specific flags set. Those flags are only respected when the signing certificate is from Azure Trusted Signing.
Various DRM and security features in Windows have niche signing requirements. For example, see this document for requirements about how anti-malware code is signed.