Managed code signing

Long story short: this blog post describes how to use a fully managed HSM (Azure Key Vault) with a purchased code signing certificate using the most affortable way I could find (approx. $ 500 / year). If you purchase a multi-year certificate, you can save some additional money (the certificate becomes cheaper). I didn’t purchase a multi-year certificate this time to minimize the cost of failure (just in case).

Introduction

Christmas came early this year. I had to renew the code signing certificate and I had the joy of figuring out what had changed since the last time I purchased a code signing certificate. Before I was able to purchase a certificate and install it locally, but nowadays this is no longer accepted by industry standards.

Purchasing a code signing certificate is already an “expensive” operation, but with the new HSM requirements, this makes it even more expensive.

I investigated using secure USB thumb drives, but from what I understood, this always requires user input when signing, which does not work for CI scenarios.

After investigating all the (expensive, managed) options, I decided to give Azure Key Vault combined with AzureSignTool a try. If you use Microsoft Action Pack (or something similar), you can save some additional money by creating the Key Vault on the subscription with free Azure credits.

Creating the Azure Key Vault

Go to Azure and create a new Key Vault resource.

Basics

Fill in everything that makes sense. For the Pricing tier, make sure to select Premium since that includes the required HSM backed keys. I chose to disable the purge protection.

Access configuration

Leave as-is (but double check yourself).

Networking

Disable public access (at a later point a specific IP used by the build agents will be added).

Review + create

Verify the settings and create the key vault.

Configuring Networking

Once the key vault is created, open it and go to Settings => Networking

Depending on your needs, you might want to choose different settings here. In order to prevent abuse, my recommendation is to limit access to the key vault as much as possible.

If access from specific build agents is required, change Allow access from to Allow public access from specific virtual networks and IP addresses.

Then add the IP address(es) of the build agent(s) to the Firewall.

Configuring RBAC

After creating the Key Vault, and despite being the owner, I was unable to create certificate requests. Therefore you need to assign the Key Vault Administrator role to the user.

Go to Access control (IAM) => Role assignments => Add => Add role assignment

Creating the certificate request

To create a certificate request, go to Objects => Certificates. Then select Generate/Import and use the values below:

Super important: make sure to select RSA-HSM, not just RSA

Then click Create

Purchasing the certificate

Select the just created certificate => Certificate operation => Download CSR

Go to your favorite certificate provider to purchase the cheapest certificate possible and use the CSR that was just created.

Note that only DigiCert allows using Azure Key Vault

In my case, I went to cheapsslsecurity.com and purchased a DigiCert Standard Validation code signing certificate for “just $438/year” (which I think is still too expensive for just a certificate).

Make sure to select the Install on Existing HSM as delivery method.

Then go through the validation process, which can take a few days to complete.

Importing the certificate

Once the validation is completed, you can import / complete the signing of the certificate.

Go to the Key Vault in the Azure Portal => Objects => Certificates => Select the certificate => Certificate operation => Merge Signed Request

The correct format for merging is the PEM format, so use these options:

Creating an Azure application

To securely access the certificate, it’s best to create a separate application.

Go to Microsoft Entra ID => Manage => App registrations => New registration

Just give it a name and click Register.

The Application ID is the Client ID that you need to pass to AzureCodeSign.

Next, go to Manage => Certificates & secrets => New client secret (might be a little hard to find, in the center of the page):

Make sure to copy the secret value immediately since it’s only available once. Don’t worry if you failed to do so, you can simply regenerate a new one.

Allowing the app registration to access the vault

Once both the Key Vault and the App Registration are created, it’s time to assign the permissions (RBAC). I prefer each application to only have access to the certificates they need access to, so I defined these permissions on the certificate level.

When assigning the roles on the certificate level, I received the “Caller is not authorized to perform action on resource” error. If you are defining the roles via the Azure portal, you need to assign the role on the vault level.

Go to your Key Vault => Objects => Certificates => Select certificate => Access control (IAM)

Then add the following role assigments to the Registered App managed identity:

Note that the registered app will not show up, you need to type in its name

Using the certificate

To use the certificate, use AzureSignTool. The tool is really well documented.

Conclusion

This method should be “future-proof” and we should be able to use this method for years to come! If you have a better / cheaper / easier way to do this, please let me know so I can update this blog post.