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
- Job function role: Key Vault Administrator
- Member: select your identity (or the one you want to be the admin following security best practices)
Creating the certificate request
To create a certificate request, go to Objects => Certificates. Then select Generate/Import and use the values below:
- Method: Generate
- Certificate name: Any name, this is a friendly name, e.g. ‘my-certificate-2024-xx-xx’
- Type of CA: Certificate issued by a non-integrated CA
- Subject: Your CN, e.g. ‘CN=MyCompany’
- Validity Period (in Months): 12
- Content type: PKCS #12
- Lifetime action type: Email all contacts
- Percentage lifetime: 80%
- Advanced Policy Configuration
- Extended Key Usages: 1.3.6.1.5.5.7.3.3
- X.509 Key Usage Flags: leave as default
- Reuse Key on Renewal: No
- Exportable Private Key: No
- Key Type: RSA-HSM
- Key Size: 4096 (is a bit more expensive, but stronger)
- Enable Certificate Transparency: Yes
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:
- Server platform: FIPS 140-2 Level 2
- File type: a single .pem file containing all the certificates
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):
- Name: give it a useful name
- Expiration: use the recommended default (of 180 days)
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:
- Key Vault Reader => required to list the certificates
- Key Vault Certificate User => required to sign
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.