I'm currently working on renewing our Microsoft Partner Network membership. As part of this process, we can optionally either pass certification exams or test our product against the Microsoft Platform Readiness tests. Naturally, I chose to test our product since this is the basis of our partner relationship with Microsoft. Last time around it was easy, our QA executed the tests and our product passed with no modifications. This time around, however, that was not the case. I ran the tests and hit a number of snags. It appears these tests have become much more rigorous. Overall, this is a good thing for the Microsoft community. For me, however, I was hoping for a lay up and instead got a center court shot at the buzzer to tie the game. Such is the life of a product developer - the only way out is through.
As part of one of my several failed attempts at passing the tests though, I became familiar with a Microsoft digital signature technology called Authenticode. I had a previous run in with Authenticode when I was trying to build a Click-Once deployment for our application. After a number of trials and tribulations I gave up on that effort and conveniently forgot about Authenticode. This time I don't have that option, so I thought I'd document the lessons I learn here so next time I remember ... and perhaps it will help someone else out.
First and foremost, Authenticode is a digitial signature technology designed to allow developers to sign their code allowing browsers (erh Internet Explorer) to verify that the developer's signature is valid and that the code has not been tampered with. In otherwords, it helps clients decide whether to trust and execute downloaded code. There is a strong tie between Authenticode and the Click-Once web deployment model that Microsoft has been pushing for the last several years because as you might expect, Click-Once applications are usually installed by launching a link in your browser.
Authenticode is based on the very wide spread security mechanism known as a digital signature, used to verify that you can trust the source of a document. The process works like this (more details can be found here):
- The developer signs code by using a digital signature tool (included with the Microsoft Windows SDK). This process generates a hash of the binary, signs the hash with the private key of the Authenticode digital certificate and signs the hash. The reason for the hash is to speed performance of verifying the signature (As you might imagine, deccrypting an entire large binary file would be an expensive client operation).
- The client receieves the binary, with the encrypted hash.
- The client generates it's own hash of the binary using the hashing algorithm identified in the signature.
- The client then decrypts the provided hash using the public key of the publisher's certifcate.
- Finally the client compares it's own hash to the provided decrypted hash to verify that the binary is authentically from the developer.
Some other steps presumably happen in this flow, such as the client verifying that the signature came from a trusted certificate authority and verfying that the signature is still valid.
Next let's review how you actually go about signing your code. If you're like me, you assume that the Visual Studio project properties would be the natural place to sign your assemblies:
But this only signs your ClickOnce assembly, if you're not using ClickOnce, you're left to your own devices. Which takes me to the Windows Software Developer toolkt and the glorious command line. If you have Visual Studio installed, then you will have the Windows SDK installed also(there are versions available for Windows 8 and Windows 8.1 also):
The program needed to sign your assemblies and installers included with the Windows SDK is called "SignTool.exe". It can be found at the following location:
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin\signtool.exe
I found that the easiest way to run the SignTool.exe is from the "Developer Command Prompt for VS2013" because it's added the PATH:
The following is an example command to sign an executible:
signtool sign -f c:\mycert.pfx -p mypassword"c:\myprogram.exe"
You can also sign other binaries such as (frankly I think you can sign any file with it):
- .cat files
- .ctl files
- .dll files
- .exe files
- .ocx files
- .msi files
After signing a file, if you right click from Windows Explorer and select "Properties" you should see a new tab called "Digital Signatures". Here's an example from a Microsoft .dll:
Additionally, it's possible to use a Post-Build event command in your project to trigger the signing of the assembly. I have to admit, I'm not especially keen on using Post-Build commands in Visual Studio. It seems to me that no matter how hard you try to make the paths dynamic, it's very difficult to ensure that they will successfully execute on every developer's machine. In the case of SignTool.exe, I'm especially concerned about ensuring that the current version and path are always correct. I want every developer who joins the team to be able to do a Get-Latest from TFS and build the project straight away. Without this, I consider our builds to be broken. Anyway, enough philosophy, here's the example build command:
call "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin\signtool.exe" sign -f "$(SolutionDir)\mycert.pfx" -p password "$(TargetPath)
I hope this brief overview of Authenticode has been helpful to you. I wish that there were a more straightforward integration between the SignTool.exe and Visual Studio. If there were and there was a nice integrated way to obtain a certificate, I'm sure more developers would use Authenticode and the internet according to Microsoft would be a safer place.
Till next time, safe travels!