Based upon information from https://github.com/dotnet/runtime/issues/51252 and https://github.com/dotnet/designs/blob/main/accepted/2021/before_bundle_build_hook.md, using the newly proposed PrepareForBundle
target, I have added the following to my .csproj
file:
<PropertyGroup>
<!-- For all build agents thus far in Azure DevOps, that is, Windows 2019, Windows 2022, Windows 2025, this has been sufficient.
Instead of trying to dynamically construct something based on the Windows SDK version, which constantly changes for each build
agent, we will just use this hard coded value. Note, this is a 32-bit executable. But for our purposes, it has been fine. -->
<SignToolPath>C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe</SignToolPath>
</PropertyGroup>
<Target Name="SignBundledFiles" BeforeTargets="GenerateSingleFileBundle" DependsOnTargets="PrepareForBundle">
<!-- Use String.Copy as a hack to then be able to use the .Compare() method. See https://stackoverflow.com/a/23626481/8169136.
All of the Microsoft assemblies are already signed. Exclude others as needed.
This is using a self-signed code signing certificate for demonstration purposes, so this exact SignTool command won't
work on your machine. Use your own certificate and replace the "code sign test" with your certificate's subject name. -->
<Exec Condition="$([System.IO.Path]::GetFileName('%(FilesToBundle.Identity)').EndsWith('.dll'))
And !$([System.String]::Copy('%(FilesToBundle.Identity)').Contains('packages\microsoft.'))
And !$([System.String]::Copy('%(FilesToBundle.Identity)').Contains('packages\system.'))"
Command=""$(SignToolPath)" sign /v /fd SHA256 /tr http://ts.ssl.com /td sha256 /n "code sign test" "%(FilesToBundle.Identity)"" />
</Target>
<Target Name="SignSelfContainedSingleFile" AfterTargets="GenerateSingleFileBundle" DependsOnTargets="SignBundledFiles">
<!-- Finally, sign the resulting self contained single file executable. -->
<Exec Command=""C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe" sign /v /fd SHA256 /n "code sign test" "$(PublishDir)$(AppHostFile)"" />
</Target>
You can read more and see the result from this blog post: