Monday, April 20, 2020

How to use VSIXSignTool

Trying to be a good developer/publisher, I sign my Visual Studio extensions.

Signing VSIXs is optional but it sends a good signal. Plus I have a code-signing certificate because I had to get one to sign NuGet packages as they MUST be signed.

But knowing how to sign a VSIX isn't obvious.
When I first wanted to sign an extension I was using Visual Studio 2017 and found the Extensibility Tools for Visual Studio from Mads Kristensen. This includes a UI for signing the generated package.

This was all good until I moved to exclusively use Visual Studio 2019. Those extensibility tools aren't supported in VS2019 so I was stuck.

To unblock myself, I ported the signing functionality, from the above-referenced tools, into a separate package that works with VS2019. A few hundred other people have also used this so I guess they were in a similar situation to me.

Fast forward to a week or so ago and I came across the NuGet package Microsoft.VSSDK.Vsixsigntool

This looked very interesting but lacked instructions on how to use it.
There's a link to some release notes but they're for "Visual Studio 2015 Update 2".
The "Project Site" link goes to the general landing page for Visual Studio Extensibility. :(

After more searching than I thought should be necessary, I eventually found
This page should be helpful but only includes some vague descriptions of what to do. :(

To try and work out how to automatically sign my generated packages using this tool, I did what I often do to solve problems relating to extension development, I searched GitHub to work out how other people had done it. Eventually, I came to this solution.

I added the following to my project file.

    <VsixSignCommand>$(VsixSignTool) sign /f $(SIGN_CERTIFICATE) /p $(SIGN_PASSWORD) /sha1 $(SIGN_CERT_HASH) /fd sha256</VsixSignCommand>
  <Target Name="AfterBuild" DependsOnTargets="CoreCompile" Condition="Exists('$(SIGN_CERTIFICATE)')">
    <Message Text="Signing $(TargetVsixContainer)" Condition="'$(Configuration)' == 'Release'" />
    <Exec Command="$(VsixSignCommand) $(MSBuildProjectDirectory)\$(TargetVsixContainer)" Condition="'$(Configuration)' == 'Release'" />

This relies on two things:

  1. The NuGet package is referenced in the project.
  2. Add environment variables for the relevant parameters.

Points of note:
  • This allows for the certificate to be in different places on different machines.
  • This means there is no need to check the certificate into the code repository.
  • The password doesn't get checked in with the source.
  • If the environment variables aren't specified the process is skipped. This is how I avoid any issue with running this on a public CI server. It only needs to be defined on the machine that builds the release version for publishing.
  • The certificate hash was found with the following command.
>certutil -p ****** -dump ./filename.pfx

I also learned that it's possible to reference environment variables in MSBuild files. However, it's necessary to restart Visual Studio for it to pick up any changes (or additions) to these.

With this all set up, every time I build a release version of my extensions they are automatically signed without me having to do anything else. This simplifies the process and avoids me needing to enter the certificate file path and password. Yay!

There are probably other solutions with online key vaults and other such things but this works for me. Maybe this will help you too.


Post a Comment