Skip to main content
Arve´s Playground

My experiences with code signing

A long time ago (in computer years) my employer wanted to sign a shrink wrapped software package using Authenticode Digital Signatures as it was a requirement for the "Designed for Microsoft Windows" logo program. This was my first experience with digital code signing, and there has been more. This is my attempt at sharing what I know.

I will talk about what codesigning is, advantages to code signing and the new requirements for storing the certificate in secure hardware. And I will explain why the best place for storing the code signing certificate given the new reqirements, is in your cloud vendors secret store. I have used Azure KeyVault as a certificate store successfully.

What is code signing? Why do you need it? #

A digital signature is an encrypted checksum of a file. The signature can be decrypted using the public key, and the resulting value can be compared with the actual checksum of the file. If it is a match, that means that the file has not been modified after it was signed, and that the file was created by a person or company in possession of the code signing certificate.

Windows EXE files is one of the file types that can be signed using the Microsoft Authenticode. Java have a similar concept for signing Java/JAR files and there are more. These specifications define what parts of the file that should be check summed, and where you should store/embed the signature and public key.

You need a digital certificate to sign code. This can be self-signed (you create it yourself) or purchased from a vendor that verifies your identity and gives you a certificate that is part of a trust chain. Signed code has more trust, especially of it if signed with the certificate of a reputable source. Some platforms will discourage or block the running of unsigned files.

Signing makes sense if you need extra proof about the origin of the file, and/or you need to check that the file is unchanged. Most code run happily without being signed and does not need to be signed.

Examples where you may need signed code:

Obtaining a certificate #

You can use a self-signed certificate for test purposes, but code signing only make sense if you get a certificate from a trusted authority.

A code signing certificate used to be a public/private key that could be distributed as a PFX file. The PFX file could easily be given to all the people that needed to sign things. New rules in effect from the summer of 2023 makes this difficult. The private key of the certificate must be non-exportable and stored in secure hardware - in a Hardware Secure Module (HSM). This is verified by the certificate vendor so a certificate can only be stored in an HSM/hardware that is supported by the vendor - your certificate vendor needs to support the HSM you want to use. This will limit what vendors you can use.

The people doing the signing must also adapt to use the certificate from HSM backed store - this may create problems challenges depending on what you want to sign.

The HSM requirement makes it almost impossible for "bad guys" to steal and misuse the certificate.

How to digitally sign a file on Windows with a local certificate #

There are different ways to sign files. I will only talk about the file types I have signed and include signing executables and other things on Windows on a local machine and in a pipeline.

Classical signing on Windows with the certificate installed locally is to run signtool. Signtool is installed with the Windows Development Kit and you run it from the command line.

The minimum options you need to provide to signtool are:

signtool /a /fd sha256 filetosign.exe

... and this will work.

signtool can sign other things as well. You can add plugins or Subject Interface Packages (SIP) that can add support for example Office files.

It can also outsource the sign operation so that it is possible to use non local certificates using an extension model. But this is complex.

The importance of timestamping #

A certificate has an expiration date. Code signing certificates have a maximum lifetime of 3 years as of now, but rules change. A signature will expire at the expiration date of the certificate used to sign it. Having an expired signature is equal or worse than having no signature at all.

There is a way to get around the expiration. Certificate vendors provide timestamp services (check your vendor for the URL), and if you send your signature to one if these servers, they "sign" your signature with the current time witch results in a timestamped signature. A timestamped signature is valid forever (or until revoked). Signtool on windows uses the /tr option to specify a timestamp service to the sign operation. A signed file that is sent to a customer should have a timestamped signature.

Timestamping takes a few seconds per signature, and the the service fails every now and then. If you sign many files then you should verify that they have been signed and add retry logic to your sign script.

A product I worked on signed 100+ files. Timestamping slowed down the build so much that we ended up with only signing a specified list of EXE files without a timestamp in the daily builds. When we create a build for customers then that is a manual run of the pipeline where we add the option that everything is signed with a timestamp.

New requirements from 2023 - Storing the certificate in a Hardware Secure Module #

From the DigiCert website "Starting on June 1 2023 industry standards will require private keys for standard code signing certificates to be stored on hardware certified as FIPS 140 Level 2, Common Criteria EAL 4+, or equivalent."

This means that a code signing certificate can no longer be requested from a browser/computer. It will be shipped on an USB device or requested/installed on a HSM that the certificate vendor supports.

Storing the certificate on an USB device like Thales SafeKey or Yubikey - this is not practical #

Given the new requirements the easiest way for the certificate vendors to give you a certificate on a secure USB device - an USB token. Accessing certificates on USB devices follow the same workflow:

These security measures are good if the certificate protects something valuable - then you want to be involved whenever the certificate is used. But it is not a good match for build pipelines running unattended. I do not want to enter a PIN/password for each of the several hundreds files we sign during a release build. There are ways to work around this, but they are unsecure in their own way.

Conclusion: Signing code in a build pipeline is not compatible with storing the certificate on a secure USB device - this applies to both USB devices I have tried: SafeNet and YubiKey

Storing the certificate in a HSM appliance - not tested and very expensive #

These products exist, they store secrets and are connected to your local network. The prices are in the $10000-$30000 range. I have not tested if they can be used from a build pipeline, I have no idea if they are easy to use or not.

Use the cloud vendors certificate store - the best option in my opinion #

You don't have to buy a HSM appliance, you can rent the service from your cloud vendor. AWS, GCP and Azure all have HSM backed (FIPS 140-2 compliant) certificate stores that supports signing operations. And it is much cheaper than an appliance - unless you have a large number of certificates. For code signing purposes an organisation should be able to manage with just one certificate.

I have only tested Azure and Azure Key Vault. The Azure cost is ~1$ per month per private key stored in an HSM + 0,0003 $ per signing operation. So having a certificate stored in an HSM would probably cost less than $20 a year. Access is managed by the cloud vendors AIM system.

Signtool can use Azure Key Vault, Google Secret Manager or other things with some setup. But the easiest way to use Azure Key Vault is to use AzueSignTool - the details will be for another post