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
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()