使用 AfterCompile 目标签署 ClickOnce 应用程序

Signing a ClickOnce application with an AfterCompile target

一周以来,我一直在努力思考 Authenticode certificates。我从 Comodo 购买了 CSC,并且我有一个 ClickOnce 应用程序,我想签名以便 SmartScreen 筛选器警告消失。

我的应用程序程序集是强名称的,我在我的项目属性中勾选了 "Sign the assembly" 框。我还在同一项目属性中勾选了 "Sign the ClickOnce manifest" 框。最后,我在我的项目文件中将以下执行设置为 AfterCompile 目标,以便使用 SHA1 和 SHA256 对可执行文件进行双重签名:

<Target Name="AfterCompile">
  <Exec Command="&quot;C:\Program Files (x86)\Windows Kits.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.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>

然后我运行下面的命令发布项目:

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

我注意到这个命令最终创建了 MyCoolApplication.exe 的三个独立版本:

不幸的是,bin\Release\app.publish 中的副本需要工作,但出于某种原因,此版本正在删除双重签名。我的理解——这可能是错误的——是它在 obj\Release 中构建东西,将它们复制到 bin\Release\app.publish,然后签署清单。然而显然还有其他事情正在发生,因为最终可执行文件上的数字签名明显发生了变化。下面是这两个文件的并排属性:

最终 single-signed/missing 时间戳版本的问题是应用程序仍然被 SmartScreen 过滤器标记,使整个过程毫无意义。我该如何解决这个问题?

更新: 阅读 this guide 后,似乎即使我正确地签名,我仍然可能会因为没有足够的 "reputation" 用于我的应用程序。不过,我想确认一下,我一开始就正确地签署了文件,而不是在追烟。 (或者,如果这表明构建过程出现故障,我想更正它!)

编辑: 这是@CodeFuller 要求的 MSBuild.exe 输出的结尾:

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

您认为二进制文件是在 obj\Release 中构建然后复制到 bin\Release\app.publish,这是正确的。对于我的测试项目,使用与 AfterCompile 目标相同的自定义步骤,一切都按预期工作。所以很可能在构建过程中出了问题。为了进一步调查,请使用 MSBuild.exe.

的输出更新您的问题

这是我的构建顺序:

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)

所以我会继续以下问题:

  • 您的 MSBuild 输出包含相同的序列还是不同?
  • 输出是否包含一些错误?
  • 构建后,obj\Release\MyCoolApplication.exeRelease\app.publish\MyCoolApplication.exe 文件的时间戳是否相等?
  • 这些文件的内容相同还是不同?

更新:

启用构建输出的诊断详细信息后,所有内容都清除了一点。

在项目设置的签名选项卡上启用 'Sign the ClickOnce manifests' 会导致 SignTool.exe 调用 bin\Release\app.publish\MyCoolApplication.exe。默认情况下,SignTool 将覆盖现有签名。这就是为什么在 AfterCompile 目标中第二次调用 SignTool 是使用 /as 键完成的 - 用于附加签名的选项。

因此,如果您想保留您的自定义签名命令,您应该禁用选项 'Sign the ClickOnce manifests' 并添加用于对清单进行签名的自定义命令。 要完成此操作,应在以下构建步骤中签署以下文件:

_DeploymentComputeClickOnceManifestInfo 目标之后: bin\Release\app.publish\MyCoolApplication.exe。文件名的宏是 "$(PublishDir)$(TargetFileName)"

_DeploymentSignClickOnceDeployment 目标之后: 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

应使用 mage.exe 工具对清单进行签名。

这里是更新的项目文件,可以满足您的需求:

<Target Name="AfterCompile">
<Exec Command="&quot;C:\Program Files (x86)\Windows Kits.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.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.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.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.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.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>

在项目属性中检查 "Sign the assembly" 和 "Sign the ClickOnce manifest" 最终会导致执行 signtool.exe(通过 SignFile MSBuild 任务),这让我觉得很可能会发生冲突对于您的 Exec 任务,由于 and/or 版本中的冗余 signtool.exe 两条路径执行的差异。一次尝试以下修改(显然跳过您已经尝试过的任何修改)。

  • 取消选中项目属性中的 "Sign the assembly" 和 "Sign the ClickOnce manifest"。
  • 只检查 "Sign the ClickOnce manifest".
  • 只检查 "Sign the assembly".
  • 注释掉您的 AfterCompile 目标并只检查 "Sign the ClickOnce manifest"。
  • 注释掉您的 AfterCompile 目标并只检查 "Sign the assembly"。
  • 注释掉您的 AfterCompile 目标并勾选 "Sign the assembly" 和 "Sign the ClickOnce manifest"
  • 将您的命令更改为以下命令并尝试使用上述排列:"C:\Program Files (x86)\MSBuild.0\Bin\MSBuild.exe" /target:Publish /p:Configuration=Release /p:Platform=AnyCPU /p:TargetFrameworkSDKToolsDirectory="C:\Program Files (x86)\Windows Kits.1\bin\x64\" MyCoolApplication.csproj