Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Signing a ClickOnce application with an AfterCompile target

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

like image 781
soapergem Avatar asked Jan 12 '17 16:01

soapergem


People also ask

How do I stop ClickOnce manifest signing?

To disable ClickOnce security settings With a project selected in Solution Explorer, on the Project menu, click Properties. Click the Security tab. Clear the Enable ClickOnce Security Settings check box.

Does ClickOnce require admin rights?

ClickOnce applications are fundamentally low impact. Applications are completely self-contained & install per-user, meaning no-admin rights are required. You don't have to worry about a ClickOnce application breaking other applications. ClickOnce applications can be deployed via web servers, file servers or CDs.

Where does a ClickOnce application get installed?

ClickOnce data directory. Every ClickOnce application installed on a local computer has a data directory, stored in the user's Documents and Settings folder. Any file included in a ClickOnce application and marked as a "data" file is copied to this directory when an application is installed.


1 Answers

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>
like image 173
CodeFuller Avatar answered Sep 23 '22 00:09

CodeFuller