I'm trying to do CI/CD in Azure DevOps with a ClickOnce application. How can I securely make my code signing certificate available during the build when using a hosted agent?
Note I'm aware you can use a script as suggested at Visual studio team services deploymen/buildt certificate error. However this approach is not secure. The certificate would be loaded into the certificate store of the account the hosted agent is running under. This would allow the agent, and hence other Azure DevOps accounts, to potentially access and use the certificate.
The solution to the issue is to override the built in task SignFile. Interestingly enough the task SignFile uses a built in function in
Microsoft.Build.Tasks.Deployment.ManifestUtilities.SecurityUtilities.SignFile
which has two overloads, one that takes a thumbprint, and one that takes a file and password.The solution is then to create a new Task that can reference the other overload. Since we cannot change the calling SignFile we need to maintain the same signature, and place the appropriate variables in the environment variables. In this case "CertificateFile" and "CertificatePassword".
Then reference those two in the overwritten SignFile. What I did was to create a new targets file (filesign.targets) and place the code there. Checked that in to my repository and referenced it from the main project file(s).
<Import Project="filesign.targets" />
This way we can also hold our key files in an Azure Key Vault, load them at built and give them a unique password just for that build.
The targets file holds the new FileSign task:
Code based on: https://gist.github.com/KirillOsenkov/4cd32c40bffd3045f77e
References: https://github.com/Microsoft/msbuild/blob/fc10ea8ce260b764bb9fa5033b327af9fefcaabe/src/Tasks/ManifestUtil/SecurityUtil.cs https://github.com/Microsoft/msbuild/blob/master/src/Tasks/SignFile.cs