Taming TFS - Digitally signing assemblies
By eidias on (tags: tfs, categories: infrastructure)Signing is a bit more tricky than strong naming. I didn’t want to use delay signing, because that requires additional configuration on development stations, so just like with strong naming – plain old simple build on the development machine and the fancy stuff on the build server.
I already have a certificate for signing but that needs to be in the build users private cert store. That can be achieved by following these steps:
- Log in as the user that is used to run the build
- Win+r and type in mmc and press “Ok”
- Press ctrl+m to add a snap in
- Select the certificates snap-in and press “Add”
- In the dialog that popped up, select “My user account” and press “Finish”
- Press “Ok” on the “Add or remove snap-ins” window
- Expand the tree on the left and right click on “Personal” node
- Select “All tasks” and then “Import…”
- Follow the steps on the wizard. When prompted to select a cert store, select the option stating “Automatically select the certificate store based on the type of the certificate”
- Press next, then finish
The certificate is now in the store and can be used to sign files.
Here is the flow I used
![]()
I decided to create a template argument that holds a list of files to sign. With the variety of projects, it could get tricky to determine which files to sign, so I went for the manual configuration.
The key activity here is the one called “Sign”. It’s of type InvokeProcess and has the following parameters specified
| Param | Value |
| Arguments | String.Format("sign /sha1 ""{0}"" /tr ""{1}"" ""{2}""", Thumbprint, TimestampingServer, file) |
| FileName | "signtool.exe" |
| WorkingDirectory | outputDirectory |
The rest is left blank.
Note that the user under which the build service is running needs to have a path to signtool in it’s environment variables, or if you’re not happy with that, you can either put a fixed path in it, or parameterize it wit a template argument.
That should be enough to sign files. If you want to check if that went ok, you can run the following command:
1: signtool verify /a <assembly>
There is a caveat here – only assemblies can be signed with this configuration. If for some reason you’d like to sign something else, you need to adjust this template to suit your requirements.
Cheers
back
JB
8/2/2016 6:01 PM
When I try this, it complains that it cannot find signtool.exe on the build server. I have specified a fixed path, "C:\Program Files (x86)\Windows Kits\8.0\bin\x64\signtool.exe" and have validated the existence of the file in windows explorer, yet I get the exception: Exception Message: File not found: C:\Program Files (x86)\Windows Kits\8.0\bin\x64\signtool.exe (type FileNotFoundException) Exception Stack Trace: Server stack trace: at Microsoft.TeamFoundation.Build.Workflow.Activities.InvokeProcess.ProcessWrapper.Start() at Microsoft.TeamFoundation.Build.Workflow.Activities.InvokeProcess.InvokeProcessInternal.RunCommand(AsyncState state) at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Object[]& outArgs) at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink) Exception rethrown at [0]: at System.Activities.Statements.Throw.Execute(CodeActivityContext context) at System.Activities.CodeActivity.InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager) at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation) Inner Exception Details: Exception Message: The system cannot find the file specified (type Win32Exception) Exception Stack Trace: at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo) at Microsoft.TeamFoundation.Build.Workflow.Activities.InvokeProcess.ProcessWrapper.Start()