Мой ответ основан на ответе ShadowChaser. разница в том, что моя цель MSBuild подпишет указанный файл или, если он не указан, он подпишет выходные данные сборки проекта, из которого была вызвана цель.
сохраните следующее в файле целей, скажем, signature.custom.targets. как только вы сохраните его, просто включите его в ваш csproj или wixproj и используйте цель, чтобы подписать ваш вывод. это также будет работать на локальной машине dev, так как файл sign.properties (это гибридный dev env, поэтому мы не хотели указывать свойства cert дважды) не существует в локальной коробке dev.
чтобы подписать файл из командной строки, вы можете
MSBuild.exe signing.custom.targets /t:SignAssembly /p:_MsiFileToSign="..\Builds\Release\Setups\yourFileToSign.msi
определение signature.custom.targets
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CertPath Condition= "'$(CertPath)' == ''">c:\dev\sign\</CertPath>
<Config Condition="'$(Config)' == ''">Release</Config>
<MSIProductVersion Condition ="'$(MSIProductVersion)' ==''">16.3</MSIProductVersion>
<MSIBuildNumber Condition ="'$(MSIBuildNumber)' ==''">3207</MSIBuildNumber>
</PropertyGroup>
<PropertyGroup Condition="'$(OutputType)'=='Library'">
<_MsiFileToSign Condition="'$(_MsiFileToSign)' ==''" >$(OutputPath)$(AssemblyName).dll</_MsiFileToSign>
</PropertyGroup>
<PropertyGroup Condition="'$(OutputType)'=='Exe'">
<_MsiFileToSign Condition="'$(_MsiFileToSign)' ==''">$(OutputPath)$(AssemblyName).exe</_MsiFileToSign>
</PropertyGroup>
<Target Name="ReadProperties">
<ReadLinesFromFile File="$(CertPath)sign.properties">
<Output TaskParameter="Lines" PropertyName="PropsInOneLine" />
</ReadLinesFromFile>
</Target>
<Target Name="CreateProperties" DependsOnTargets="ReadProperties">
<PropertyGroup>
<SignToolPath>$(CertPath)signtool.exe</SignToolPath>
<AuthenticodeCertFile Condition="'$(PropsInOneLine)' != ''">$(CertPath)$([System.String]::Copy($(PropsInOneLine)).Split(';')[0].Split('=')[1])</AuthenticodeCertFile>
<AuthenticodePassword Condition="'$(PropsInOneLine)' != ''">$([System.String]::Copy($(PropsInOneLine)).Split(';')[2].Split('=')[1])</AuthenticodePassword>
<AuthenticodeTimestamp Condition="'$(PropsInOneLine)' != ''">$([System.String]::Copy($(PropsInOneLine)).Split(';')[4].Split('=')[1])</AuthenticodeTimestamp>
</PropertyGroup>
</Target>
<Target Name="SignAssembly"
DependsOnTargets="CreateProperties" >
<Message Text=" File Name to sign= $(_MsiFileToSign)" />
<Exec Command=""$(SignToolPath)" sign /f "$(AuthenticodeCertFile)" /p "$(AuthenticodePassword)" /t $(AuthenticodeTimestamp) /v "$(_MsiFileToSign)"" Condition="'$(PropsInOneLine)' != ''" />
</Target>
<Target Name="SignMsi"
DependsOnTargets="CreateProperties" >
<PropertyGroup>
<_MsiFileToSign>$(TargetPath)</_MsiFileToSign>
</PropertyGroup>
<Message Text=" File Name to sign= $(_MsiFileToSign)" />
<Exec Command=""$(SignToolPath)" sign /f "$(AuthenticodeCertFile)" /p "$(AuthenticodePassword)" /t $(AuthenticodeTimestamp) /v "$(_MsiFileToSign)"" Condition="'$(PropsInOneLine)' != ''" />
</Target>
</Project>
включая его в ваш csproj
<Import Project="$(SolutionDir)signing.custom.targets" />
<Target Name="AfterBuild" DependsOnTargets="SignAssembly">
</Target>
чтобы включить его в свой wixproj
<Import Project="$(SolutionDir)signing.custom.targets" />
<Target Name="AfterBuild" DependsOnTargets="SignAssembly">
</Target>