5

I've been trying to wrap my mind around Authenticode certificates for a week now. I purchased a CSC from Comodo and I've got a ClickOnce application I'd like to sign so that the SmartScreen Filter warnings go away.

My application assembly is strong-named and I've ticked the box to "Sign the assembly" in my Project Properties. I've also ticked the box to "Sign the ClickOnce manifest" in the same Project Properties. And finally, I have the following executions set up as AfterCompile targets in my project file, in order to dual-sign the executable with both SHA1 and SHA256:

<Target Name="AfterCompile">
  <Exec Command="&quot;C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe&quot; sign /f &quot;$(ProjectDir)certificate.pfx&quot; /p mypassword /t http://timestamp.comodoca.com /v &quot;$(ProjectDir)obj\$(ConfigurationName)\$(TargetFileName)&quot;" />
  <Exec Command="&quot;C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe&quot; sign /f &quot;$(ProjectDir)certificate.pfx&quot; /p mypassword /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 /as /v &quot;$(ProjectDir)obj\$(ConfigurationName)\$(TargetFileName)&quot;" />
</Target>

Then I run the following the command to publish the project:

"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" /target:Publish /p:Configuration=Release /p:Platform=AnyCPU MyCoolApplication.csproj

What I've noticed is that this command ultimately creates three separate versions of MyCoolApplication.exe:

  • It builds one copy in bin\Release, which is unsigned
  • It builds another copy in obj\Release, which is dual-signed
  • It builds a final copy in bin\Release\app.publish, which is signed only once and appears to be missing a timestamp

Unfortunately it's the copy in bin\Release\app.publish that needs to work, but for some reason this version is removing the dual signature. My understanding – which could be wrong – was that it built things in obj\Release, copied them to bin\Release\app.publish, and then signed the manifest. However clearly something else is going on, as the digital signature on the final executable is clearly changed. Here's the properties of those two files side-by-side:

dual-signed single-signed, no timestamp

The problem with the final single-signed/missing timestamp version is that the application still gets flagged with the SmartScreen filter, rendering the whole process pointless. How can I fix this?

UPDATE: After reading this guide, it seems that even if I sign things properly, I may still bump into the SmartScreen Filter by virtue of not having enough "reputation" for my application. However I would like to confirm that I have signed things properly in the first place, and am not chasing smoke. (Or if this is indicative of a breakdown in the build process, I want to correct that!)

EDIT: Here is the end of the MSBuild.exe output, which @CodeFuller has requested:

AfterCompile:

"signtool.exe" sign /f "certificate.pfx" /p mypassword /t http://timestamp.comodoca.com /v "MyCoolApplication\obj\Release\MyCoolApplication.exe"

The following certificate was selected:

...

Done Adding Additional Store

Successfully signed: MyCoolApplication\obj\Release\MyCoolApplication.exe

Number of files successfully Signed: 1

Number of warnings: 0

Number of errors: 0

"signtool.exe" sign /f "MyCoolApp lication\certificate.pfx" /p mypassword /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 /as /v "MyCoolApplication\obj\Release\MyCoolApplication.exe"

The following certificate was selected:

...

Done Adding Additional Store

Successfully signed: MyCoolApplication\obj\Release\MyCoolApplication.exe

Number of files successfully Signed: 1

Number of warnings: 0

Number of errors: 0

_DeploymentComputeClickOnceManifestInfo:

Creating directory "bin\Release\app.publish".

Copying file from "obj\Release\MyCoolApplication.exe" to "bin\Release\app.publish\MyCoolApplication.exe".

_CopyAppConfigFile:

Copying file from "App.config" to "bin\Release\MyCoolApplication.exe.config".

_CopyManifestFiles:

Copying file from "obj\Release\MyCoolApplication.exe.manifest" to "bin\Release\MyCoolApplication.exe.manifest".

MyCoolApplication -> C:\Users\Gordon\Documents\Visual Studio 2015\Projects\MyCoolApplication\MyCoolApplication\bin\Release\MyCoolApplication.exe.manifest

Copying file from "obj\Release\MyCoolApplication.application" to "bin\Release\MyCoolApplication.application".

MyCoolApplication -> C:\Users\Gordon\Documents\Visual Studio 2015\Projects\MyCoolApplication\MyCoolApplication\bin\Release\MyCoolApplication.application

CopyFilesToOutputDirectory:

Copying file from "obj\Release\MyCoolApplication.exe" to "bin\Release\MyCoolApplication.exe".

MyCoolApplication -> C:\Users\Gordon\Documents\Visual Studio 2015\Projects\MyCoolApplication\MyCoolApplication\bin\Release\MyCoolApplication.exe

Copying file from "obj\Release\MyCoolApplication.pdb" to "bin\Release\MyCoolApplication.pdb".

_CopyFilesToPublishFolder:

Creating directory "bin\Release\app.publish\Application Files\MyCoolApplication_1_0_0_0".

Copying file from "bin\Release\MyCoolApplication.exe.manifest" to "bin\Release\app.publish\Application Files\MyCoolApplication_1_0_0_0\MyCoolApplication.exe.manifest". Copying file from "bin\Release\app.publish\MyCoolApplication.exe" to "bin\Release\app.publish\Application Files\MyCoolApplication_1_0_0_0\MyCoolApplication.exe.deploy". Copying file from "App.config" to "bin\Release\app.publish\Application Files\MyCoolApplication_1_0_0_0\MyCoolApplication.exe.config.deploy". Copying file from "triforce.ico" to "bin\Release\app.publish\Application Files\MyCoolApplication_1_0_0_0\triforce.ico.deploy". Done Building Project "C:\Users\Gordon\Documents\Visual Studio 2015\Projects\MyCoolApplication\MyCoolApplication\MyCoolApplication.csproj" (Publish target(s)).

Build succeeded. 0 Warning(s) 0 Error(s)

Time Elapsed 00:00:06.53

soapergem
  • 9,263
  • 18
  • 96
  • 152
  • 2
    My experience is you need an Extended Validation (EV) code signing certificate to pass SmartScreen (more expensive...). We have a click once app that we sign with such a certificate and it works fine (we have no custom step with signtool like you do, we just use the regular VS project ClickOnce properties to sign it). Drawback is you're not able to automate it with a pfx and a password: http://stackoverflow.com/questions/17927895/automate-extended-validation-ev-code-signing – Simon Mourier Jan 18 '17 at 07:40
  • 1
    @SimonMourier I am aware of the advantages of EV certificates. However, from the Microsoft website it seems that even though a standard (not-EV) CSC doesn't come with the benefit of instantaneous perfect Reputation, it still builds reputation faster than unsigned code, and the reputation builds towards your Publisher Name rather than the individual app (as it would with unsigned code). Less ideal than EV, yes, but EV has other drawbacks besides just cost -- like the fact that you can't virtualize your build server because you need the hardware dongle attached to sign. – soapergem Jan 18 '17 at 16:01
  • 1
    Still, my question here is more about the fact that something in the MSBuild seems to be silently changing/re-signing my executable after the "AfterCompile" target I have set up to do the signing finishes, and how I might go about fixing that. If it is as I suspect, that something in the process is changing the file unexpectedly, I imagine I would be plagued with that problem regardless of whether my CSC includes EV or not. – soapergem Jan 18 '17 at 16:02
  • Concerning EV, I agree it's totally agree it's not super clear if it's needed or not, anyway, I can build successfully with it in a virtual machine hosted in another server (I connect via remote desktop, the EV cert resides in an USB key that crosses boundaries successfully provided the SafeNet client is installed on both my machine and the VM I guess). I'm not clear why you need custom steps for signing your app with ClickOnce. – Simon Mourier Jan 18 '17 at 18:13
  • Check my updated answer – CodeFuller Jan 19 '17 at 05:41

2 Answers2

6

You're correct in your expectation that binaries are built in obj\Release and then copied to bin\Release\app.publish. And for my test project with the same custom steps for AfterCompile target everything works as expected. So most likely something goes wrong during the build. For further investigation, please update your question with output of MSBuild.exe.

Here is my build sequence:

AfterCompile:
"signtool.exe" sign /f "MyKey.pfx" /p mypassword /t http://timestamp.comodoca.com /v "MyCoolApplication\obj\Release\MyCoolApplication.exe"
...

Successfully signed: MyCoolApplication\obj\Release\MyCoolApplication.exe

Number of files successfully Signed: 1

Number of warnings: 0

Number of errors: 0

"signtool.exe" sign /f "MyKey.pfx" /p mypassword /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 /as /v "MyCoolApplication\obj\Release\MyCoolApplication.exe"
...

Successfully signed: MyCoolApplication\obj\Release\MyCoolApplication.exe

Number of files successfully Signed: 1

Number of warnings: 0

Number of errors: 0

CleanPublishFolder:
Removing directory "bin\Release\app.publish\".
_DeploymentComputeClickOnceManifestInfo:
Creating directory "bin\Release\app.publish".
Copying file from "obj\Release\MyCoolApplication.exe" to "bin\Release\app.publish\MyCoolApplication.exe".
...
Build succeeded.
0 Warning(s)
0 Error(s)

enter image description here

So I'd continue with following questions:

  • Does your MSBuild output contains the same sequence or it differs?
  • Does the output contain some errors?
  • After the build, are the timestamps for obj\Release\MyCoolApplication.exe and Release\app.publish\MyCoolApplication.exe files equal?
  • Is the content of these files equal or they differ?

UPDATE:

After enabling Diagnostic verbosity of build output all cleared up a bit.

Enabling of 'Sign the ClickOnce manifests' on Signing tab of project settings leads to SignTool.exe call for bin\Release\app.publish\MyCoolApplication.exe. By default SignTool will overwrite existing signature. That's why second call of SignTool in your AfterCompile target is done with /as key - the option for appending the signature.

So if you want to keep your custom signing commands you should disable option 'Sign the ClickOnce manifests' and add custom commands for signing the manifest. To complete this following files should be signed on following build steps:

After _DeploymentComputeClickOnceManifestInfo target: bin\Release\app.publish\MyCoolApplication.exe. The macro for filename is "$(PublishDir)$(TargetFileName)"

After _DeploymentSignClickOnceDeployment target: bin\Release\app.publish\Application Files\MyCoolApplication_1_0_0_3\MyCoolApplication.exe.manifest - "$(_DeploymentApplicationDir)$(_DeploymentTargetApplicationManifestFileName)" bin\Release\app.publish\MyCoolApplication.application - "$(PublishDir)$(TargetDeployManifestFileName)" bin\Release\app.publish\setup.exe - $(PublishDir)\setup.exe

Manifest should be signed with mage.exe tool.

Here is updated project file that achieves what you need:

<Target Name="AfterCompile">
<Exec Command="&quot;C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe&quot; sign /f &quot;$(ProjectDir)certificate.pfx&quot; /p mypassword /t http://timestamp.comodoca.com /v &quot;$(ProjectDir)obj\$(ConfigurationName)\$(TargetFileName)&quot;" />
<Exec Command="&quot;C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe&quot; sign /f &quot;$(ProjectDir)certificate.pfx&quot; /p mypassword /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 /as /v &quot;$(ProjectDir)obj\$(ConfigurationName)\$(TargetFileName)&quot;" />
</Target>

<Target Name="SignAssembly" AfterTargets="_DeploymentComputeClickOnceManifestInfo">
<Exec Command="&quot;C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe&quot; sign /f &quot;$(ProjectDir)certificate.pfx&quot; /p mypassword /t http://timestamp.comodoca.com /v &quot;$(PublishDir)$(TargetFileName)&quot;" />
<Exec Command="&quot;C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe&quot; sign /f &quot;$(ProjectDir)certificate.pfx&quot; /p mypassword /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 /as /v &quot;$(PublishDir)$(TargetFileName)&quot;" />
</Target>

<Target Name="SignManifest" AfterTargets="_DeploymentSignClickOnceDeployment">
<Exec Command="&quot;c:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\mage.exe&quot; -Sign &quot;$(_DeploymentApplicationDir)$(_DeploymentTargetApplicationManifestFileName)&quot; -CertFile &quot;$(ProjectDir)certificate.pfx&quot; -Password mypassword -TimeStampUri http://timestamp.comodoca.com" />
<Exec Command="&quot;c:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\mage.exe&quot; -Sign &quot;$(PublishDir)$(TargetDeployManifestFileName)&quot; -CertFile &quot;$(ProjectDir)certificate.pfx&quot; -Password mypassword -TimeStampUri http://timestamp.comodoca.com" />
<Exec Command="&quot;C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe&quot; sign /f &quot;$(ProjectDir)certificate.pfx&quot; /p mypassword /t http://timestamp.comodoca.com /v &quot;$(PublishDir)\setup.exe&quot;" />
<Exec Command="&quot;C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe&quot; sign /f &quot;$(ProjectDir)certificate.pfx&quot; /p mypassword /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 /as /v &quot;$(PublishDir)\setup.exe&quot;" />
</Target>
CodeFuller
  • 30,317
  • 3
  • 63
  • 79
  • 1
    (1.) My build output is virtually the same as what you've posted. (2.) No errors. (3.) The file modification times for the two EXEs are indeed the same. However if I look at the "Digital Signatures" tab of their properties it is just like the pictures I've posted above, where one has 2 signatures with time stamps, and the other has 1 signature without a time stamp. (4.) Their file sizes differ by about 7 KB. – soapergem Jan 17 '17 at 15:36
  • `/as` option is an invalid with `signtool.exe` from `\Microsoft SDKs\Windows\v7.1\Bin` – Aikanáro Jun 18 '20 at 17:58
1

Checking "Sign the assembly" and "Sign the ClickOnce manifest" in the Project Properties ultimately results in execution of signtool.exe (via the SignFile MSBuild task), which strikes me as likely to conflict with your Exec tasks, due to redundancy and/or differences in the version of signtool.exe the two pathways execute. Try the following modifications one at a time (obviously skip any you have already tried).

  • Uncheck both "Sign the assembly" and "Sign the ClickOnce manifest" in the Project Properties.
  • Check just "Sign the ClickOnce manifest".
  • Check just "Sign the assembly".
  • Comment out your AfterCompile target and check just "Sign the ClickOnce manifest".
  • Comment out your AfterCompile target and check just "Sign the assembly".
  • Comment out your AfterCompile target and check both "Sign the assembly" and "Sign the ClickOnce manifest"
  • Change your command to the following and try it with the above permutations: "C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" /target:Publish /p:Configuration=Release /p:Platform=AnyCPU /p:TargetFrameworkSDKToolsDirectory="C:\Program Files (x86)\Windows Kits\8.1\bin\x64\" MyCoolApplication.csproj
weir
  • 4,521
  • 2
  • 29
  • 42