Code Signing on Windows

When you release software out to the world, one of many important aspects of your build/release process, is to sign files before you package them into an “EXE” or an “MSI” or any other means of packaging.

If you are wondering why Code Signing is important – this article provides a good overview on that.

There are a couple of ways you can sign files on Windows

Signing using SignTool

SignTool is available as part of Windows SDK. Download link is https://go.microsoft.com/fwlink/p/?linkid=84091

Learn more about SignTool here Assuming you already have a valid certificate (.pfx) file at your disposal, you can sign a file like so

SignTool.exe sign /f <path to cert.pfx> /p <password> /t <timestamp_url> <fullpath to file>

NOTE: SignTool also supports commands like timestamp, verify, catdb etc., Refer here for more info on these commands.

This method is great for signing individual files or for one-off file signing. However, if you have incorporated file signing as part of your build process, then using this method exposes the certificate password in scripts, and for obvious reasons, we don’t want that. One of the solutions is to use PowerShell (v2.0 or greater) provided Authenticode cmdlets.

Signing with Authenticode powershell cmdlets

Powershell provides two cmdlets for getting and setting signatures on files (available on Powershell versions 6.0, Core 6.2.0 also)

  1. Get-AuthenticodeSignature
  2. Set-AuthenticodeSignature

As the name suggests, Get-AuthenticodeSignature cmdlet queries Signature information from a file.

The Status property on the returned object contains values such as Valid,hashMismatch,Invalid,UnKnownError, to determine the Signature status of a file.

E.g. The following command will return all dlls, under C:\Build folder, with a Valid Digital Signature

PS C:\> Get-AuthenticodeSignature -filepath C:\Build\*.dll `
            | Where-Object {$_.status –eq “Valid”}

Set-AuthenticodeSignature, as you would’ve guessed, can sign files - using a Certificate (.pfx) and a TimestampServer URL.

You can load a certificate using Get-PfxCertificate cmdlet

Get-PfxCertificate returns both the certificate and a private key.

Get-PfxCertificate -FilePath "C:\windows\system32\Test.pfx"

Password: ******
Signer Certificate:      David Chew (Self Certificate)
Time Certificate:
Time Stamp:
Path:                    C:\windows\system32\zap.pfx

It’s always a good idea to capture the certificate info to a variable, as this step may require you to enter a password if the certificate has one.

PS C:\> $cert = Get-PfxCertificate C:\Test\Mysign.pfx
Password: ******
...
PS C:\> Set-AuthenticodeSignature -Certificate $cert `
            -Filepath C:\Build\extraordinary.dll `
            –TimestampServer “http://tsa.starfieldtech.com”

If you need to use this in your build step in your CI/CD pipeline then you could automate the password entering part using PSCredentials and Convert*-SecureString cmdlets. Here are a few pointers/examples that explain on acheiving this.

Purchase Code Signing Certs

Code Signing certificates can be purchased from vendors like DigiCert, Thawte, ssl.com to name a few.

NOTE: I’m not vouching for any these vendors. Please do your own research and choose the one that best fits your need

However, in many organizations, IT Administrators hold the lock and key for all purchased certificates, and usually, for good reasons, you’ll not be provided access to those certificates.

If you find yourself in this scenario then you can request your IT Administrator to install the certificate on your build server’s personal store. Once installed, you can verify this by launching certmgr.msc from run command and clicking on Personal folder.

Once the certificate is installed in your personal store, you can now query this certificate and load it into an object and pass it to Set-AuthenticodeSignature.

You can also leverage Powershell’s Providers to Query certificates from your certificate store (Cert: drive is the drive exposed by the certificate provider)

PS C:\> $cert = Get-ChildItem -Path cert:\CurrentUser\my `
                –CodeSigningCert
PS C:\> Set-AuthenticodeSignature -Certificate $cert `
            -Filepath C:\Build\extraordinary.dll `
            –TimestampServer “http://tsa.starfieldtech.com”

I like this approach better because it removes the need for entering passwords for certificates in clear-text.

A little freebie – find below a list of 4 valid timestamp server URLs (at the time of this writing)

if you have, say many files to sign, and if you build often and have signing enabled on every build (usually when you have CD enabled) then, you’ll most likely pound a timestamp server with many timestamp requests, which will often result in timeout errors – one solution is to load the below list of timestamp servers to an array and use the Get-Random cmdlet to sign files using a random (out of these 4) servers to distribute the timestamp request load.

Hope this helps!

Happy Code Signing!