Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to Sign ClickOnce Deployment if ClickOnce Code Signing Function in Visual Studio Doesn't Work

0.00/5 (No votes)
15 Jan 2018 1  
How to properly sign ClickOnce deployment in a way that actually works

Introduction

Recently, I was developing a WPF desktop application. This application was to be available for download from a website so we needed an installer to install this app on customer computers. The app and the installer had to be code signed to avoid being blocked by SmartScreen and antivirus programs.

I decided to use ClickOnce technology because it seemed like the easiest approach to generate installer and it provides automatic updates. Unfortunately, it soon turned out that code signing a ClickOnce deployment is quite a difficult task. There are some tips on the Internet about how to sign a ClickOnce deployment, but they do not cover the whole subject so I decided to write how I solved this problem by myself.

Background

The ClickOnce deployment consists of four parts:

  1. The application files
  2. Application manifest
  3. Deployment manifest
  4. Boostrapper file (setup.exe)

It's important that these four elements have to be signed separately. Many developers are only signing application files or only manifests. It can lead to deployment errors and the SmartScreen filter will still block the improperly signed application.

Signing ClickOnce Deployment in Visual Studio

First of all, do not use this:

These options should theoretically sign your ClickOnce manifest automatically but they don't allow you to specify command line arguments for signtool so they just don't work with some electronic signatures. What you have to do is to manually edit your project file and add some deployment targets.

1. Sign the application file

Right click your project and select Unload project. Then right click on the unloaded project and select "Edit <projectname>.csproj". The code editor appears.

Scroll to the bottom and add a new target just before the last closing </Project> tag:

<Target Name="AfterCompile" Condition="
'$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">

  <Exec Command="&quot;C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe&quot;
  sign /n &quot;InsertNameHere&quot; /t http://yourtimestampuri /fd sha1
  /v &quot;$(ProjectDir)obj\$(ConfigurationName)\$(TargetFileName)&quot;" />

</Target>

Of course, you have to replace "C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe" with the correct signtool path in your OS and provide valid command line parameters for signtool. Usually electronic signature resellers provide tutorials on how to properly use signtool.

The above code snippet signs the application file. Now you have to sign two manifests and the boostrapper file.

2. Sign the application manifest

Add a second target to .csproj file:
  <Target Name="SignManifest" AfterTargets="_DeploymentSignClickOnceDeployment" 

   Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">

</Target>

Now we will add four commands to this target tag. First of all, we have to sign the application manifest:

<Exec Command="&quot;C:\Program Files 
(x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.2 Tools\mage.exe&quot; 
-Sign &quot;$(_DeploymentApplicationDir)$(_DeploymentTargetApplicationManifestFileName)&quot; 
-CertHash INSERTVALIDHASHHERE -TimeStampUri http://yourtimestampuri" />

Mage.exe is a tool for editing and signing ClickOnce manifests. You will have to find it on your system and provide a valid path to that file.

3. Update deployment manifest to reference the signed version of application manifest

The above command signs the application manifest. You now have a signed application manifest and unsigned deployment manifest. There's no use to sign the deployment manifest now because it still references the unsigned version of the application manifest. You have to update the generated deployment manifest to reference the signed version of the application manifest:

<Exec Command="&quot;C:\Program Files 
(x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.2 Tools\mage.exe&quot; 
-Update &quot;$(PublishDir)$(TargetDeployManifestFileName)&quot; 
-AppManifest &quot;$(_DeploymentApplicationDir)$(_DeploymentTargetApplicationManifestFileName)&quot;" />

4. Sign the deployment manifest

The deployment manifest now references the correct application manifest so we can sign it now:

<Exec Command="&quot;C:\Program Files 
(x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.2 Tools\mage.exe&quot; 
-Sign &quot;$(PublishDir)$(TargetDeployManifestFileName)&quot; 
-CertHash INSERTVALIDHASHHERE -TimeStampUri http://yourtimestampuri" />

5. Sign the bootstrapper file

We have both manifests signed. Now we just have to sign the setup.exe file:

<Exec Command="&quot;C:\Program Files 
(x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe&quot; 
sign /n &quot;CertificateName&quot; /t http://yourtimestampuri /fd sha1 
/v &quot;$(PublishDir)\setup.exe&quot;" />

That's all. The all four elements of the ClickOnce deployment are now properly signed.

Summary

This is the full code that you have to insert to .csproj file:

  <Target Name="AfterCompile" Condition=" 
'$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <Exec Command="&quot;C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe&quot; 
     sign /n &quot;InsertNameHere&quot; /t http://yourtimestampuri /fd sha1 
     /v &quot;$(ProjectDir)obj\$(ConfigurationName)\$(TargetFileName)&quot;" />
  </Target>

  <Target Name="SignManifest" AfterTargets="_DeploymentSignClickOnceDeployment" 

   Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <Exec Command="&quot;C:\Program Files 
    (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.2 Tools\mage.exe&quot; 
    -Sign &quot;$(_DeploymentApplicationDir)$(_DeploymentTargetApplicationManifestFileName)&quot; 
    -CertHash INSERTVALIDHASHHERE -TimeStampUri http://yourtimestampuri" />
    <Exec Command="&quot;C:\Program Files 
    (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.2 Tools\mage.exe&quot; 
    -Update &quot;$(PublishDir)$(TargetDeployManifestFileName)&quot; 
    -AppManifest &quot;$(_DeploymentApplicationDir)$
    (_DeploymentTargetApplicationManifestFileName)&quot;" />
    <Exec Command="&quot;C:\Program Files 
    (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.2 Tools\mage.exe&quot; 
    -Sign &quot;$(PublishDir)$(TargetDeployManifestFileName)&quot; 
    -CertHash INSERTVALIDHASHHERE -TimeStampUri http://yourtimestampuri" />
    <Exec Command="&quot;C:\Program Files 
     (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe&quot; 
     sign /n &quot;CertificateName&quot; /t http://yourtimestampuri /fd sha1 
     /v &quot;$(PublishDir)\setup.exe&quot;" />
  </Target>

Points of Interest

Electronic signature resellers offer you two types of code signing certificates: standard code signing and extended validation code signing. The second type instantly removes SmartScreen warning when customers try to install your application. The first type of certificate removes SmartScreen after some time when your application gathers reputation points. In my opinion, Extended Validation certificates are not worth buying. We use Standard certificate and the SmartScreen filter message disappeared after about two weeks since the product release. We haven't seen a SmartScreen message since then.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here