I have an Authenticode certificate (.pfx) which I use to sign executables.
How can I configure Team Build so that it signs every single executable (.exe, .dll, ...) automatically while building the project?
Here's the method we use:
Unload the WiX project and select Edit
Scroll to the bottom, where you can find <Import Project="$(WixTargetsPath)" />
Add a new line immediately above it: <Import Project="ProjectName.custom.targets" />
We use the naming convention "ProjectName.custom.targets", but the file can be named anything you want.
Create a new XML file named ProjectName.custom.Targets and place the following code into it:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- replace the contents of this with your private test authenticode certificate -->
<AuthenticodeCertFile Condition="'$(AuthenticodeCertFile)' == ''">$(MSBuildProjectDirectory)\AuthenticodeTest.pfx</AuthenticodeCertFile>
</PropertyGroup>
<!-- this gets the path to signtool.exe and places it in the _SignToolSdkPath property -->
<Target Name="_GetSignToolPath">
<GetFrameworkSdkPath>
<Output TaskParameter="Path" PropertyName="_SignToolSdkPath" />
</GetFrameworkSdkPath>
<PropertyGroup>
<_SignToolPath>$(_SignToolSdkPath)bin\signtool.exe</_SignToolPath>
</PropertyGroup>
</Target>
<!-- This gets a list of all of the "referenced" assembies used by the installer project -->
<!-- Unfortunately, I cheated and used an "internal" item list - you could replace this with each specific assembly but it gets complicated if your build output is redirected -->
<Target Name="_GetSourceAssembliesToSign" DependsOnTargets="ResolveReferences">
<!-- Kludge - not supposed to target internal items, but there are no other options -->
<CreateItem Include="@(_ResolvedProjectReferencePaths)">
<Output ItemName="_SourceAssemblyToSign" TaskParameter="Include" />
</CreateItem>
</Target>
<!-- This signs the assemblies in the @(_SourceAssemblyToSign) item group -->
<!-- Note that it only executes when build output is redirected ie/ on TFS Build or when OutDir is changed -->
<!-- Authenticode timestamp is optional - doesn't make sense to timestamp the test certificate -->
<Target Name="_AuthenticodeSignSourceAssemblies" AfterTargets="BeforeBuild" DependsOnTargets="_GetSignToolPath;_GetSourceAssembliesToSign" Condition="'$(AuthenticodeCertFile)' != '' and '$(OutDir)' != '$(OutputPath)'">
<Exec Command=""$(_SignToolPath)" sign /f "$(AuthenticodeCertFile)" /p "$(AuthenticodePassword)" /t $(AuthenticodeTimestamp) /v "%(_SourceAssemblyToSign.Identity)"" Condition="'$(AuthenticodeTimestamp)' != ''" />
<Exec Command=""$(_SignToolPath)" sign /f "$(AuthenticodeCertFile)" /p "$(AuthenticodePassword)" /v "%(_SourceAssemblyToSign.Identity)"" Condition="'$(AuthenticodeTimestamp)' == ''" />
</Target>
<!-- This signs the MSI file itself -->
<!-- Note that additional changes may be needed if your CAB files are separate - those would need to be signed as well -->
<!-- Note that it only executes when build output is redirected ie/ on TFS Build or when OutDir is changed -->
<Target Name="_AuthenticodeSignMsi" AfterTargets="SignMsi" DependsOnTargets="_GetSignToolPath" Condition="'$(AuthenticodeCertFile)' != '' and '$(OutDir)' != '$(OutputPath)'">
<PropertyGroup>
<_MsiFileToSign>$(TargetDir)%(CultureGroup.OutputFolder)$(TargetName)$(TargetExt) </_MsiFileToSign>
</PropertyGroup>
<Exec Command=""$(_SignToolPath)" sign /f "$(AuthenticodeCertFile)" /p "$(AuthenticodePassword)" /t $(AuthenticodeTimestamp) /v "$(_MsiFileToSign)"" Condition="'$(AuthenticodeTimestamp)' != ''" />
<Exec Command=""$(_SignToolPath)" sign /f "$(AuthenticodeCertFile)" /p "$(AuthenticodePassword)" /v "$(_MsiFileToSign)"" Condition="'$(AuthenticodeTimestamp)' == ''" />
</Target>
</Project>
Create a test authenticode certificate (we named ours AuthenticodeTest.pfx) and place it in source control - the path to it is set in the AuthenticodeCertFile property. To test it out, run msbuild at command line and change the OutDir property - ie/ msbuild Test.sln /p:OutDir=C:\Test
Some customizations will be needed if:
To run your final build select "Queue New Build" in TFS. Click "Parameters" and expand "Advanced". Under "MSBuild Arguments" add /p:AuthenticodeCertFile=ProductionCertFile.pfx /p:AuthenticodePassword=Secret
. Note that this may not be entirely secure - it could be tricky to have the build agent find the PFX file without checking it in and the password could be logged in the build output. Alternately you could create a special locked down build agent for this, or run the build locally at command line - but obviously that wouldn't be a "clean room" environment. It may be worth creating a special locked down "clean" server specifically for that purpose.