使用signtool.exe自动化代码签名,但不存储证书或密码。

18

我在Visual Studio 2013中有一个C#/.NET 4.5 x64项目。多个开发人员共同开发此项目,因此使用Git管理代码。我使用signtool.exe为编译后的.dll和.exe文件签名。我的公司购买了代码签名证书,如果我手动从命令行进行签名,如下所示:

signtool.exe sign /f cert.p12 /p "password" "compiled.dll"

......那么看起来一切都很顺利:我收到了成功消息,编译后的DLL文件在Windows资源管理器中显示为已正确签名。因此,实际签名过程没有任何问题。

但是,证书及其密码不能存放在Git中。它们将通过专门渠道提供给项目中的所有开发人员。我可以假设每个开发人员在构建项目时都会将证书存储在计算机的预定义位置,并且他将知道证书的密码。

因此,我的问题是:如何配置Visual Studio 2013以自动对其编译输出进行签名而不保留证书或其密码在Git中呢? 我希望它能够简单地工作,一旦开发人员在预定义路径(或导入等)中获得了证书,并且基于开发人员知道证书的密码的假设,只需在Visual Studio 2013中点击“生成”即可完成构建和签名,无需进一步操作。

如果签名过程可以是非交互式的(无需密码提示),那就更好了。最终,这将是连续集成(CI)服务器的一部分,该服务器可能也会对其输出进行签名,由于全程自动化,没有人员在场输入密码。不过,目前我接受任何解决方案。

我的证书是PKCS#12格式,受密码保护。Windows声称它未标记为可导出。

6个回答

17

另一种方法是将证书导入每个开发人员的私有证书存储中,然后像这样使用 signtool 的 thumbprint:

signtool ... /sha1 'hex thumbprint' ...

在导入证书时只需要密码,构建过程中不需要。


这种方法很有效,解决了我在代码签名方面遇到的几个安全问题。现在我的项目不需要证书或密码了。我只需将其安装在证书存储中,并在各处使用指纹即可。感谢您的提示!非常好的建议。 - Sie Raybould

15

我以前使用过的一种解决方案与@ Mikko的答案类似,但分为两个部分:

  1. 一个本地的非受控脚本,只是设置包含密码的环境变量的文件。这是你给每个开发人员的文件。

@echo off
set SIGNPASS=whatever
  • 一个受源代码控制的脚本,调用之前的脚本并进行实际签名。

  • @echo off
    setlocal
    call "C:\local\signing_password.bat"
    "C:\toolpath\signtool.exe" sign /f "c:\certpath\cert.p12" /p "%SIGNPASS%" "%1"
    endlocal
    

    setlocal/endlocal语句可以确保在手动运行脚本时,密码不会泄露到环境中。

    "%1"是在Post Build步骤中作为脚本参数传递的可执行文件路径。


    我没有想到可以这样分割;我有一些使用文本文件的想法,但环境变量似乎更好。谢谢你;我明天会试试这个。 - user3466413
    2
    你在 signtool 路径后面缺少了 "sign" 命令。 - scone

    8
    我不能对Daniel Schlößer的回答进行补充——在我看来,那确实是最好的答案。一旦证书被添加到证书存储中,就不需要密码和pfx来签名。
    然而,我想要添加以下评论:
    1. 证书必须正确安装在证书存储中(我使用了.pfx文件和密码)
    2. signtool.exe必须支持/sha1命令行选项(我使用的是10.0.16299版本)
    3. 对于我的证书,我花费了很长时间才意识到我需要使用/sm开关来指定“机器存储”而不是“用户存储”
    4. Thumbprint可以在证书属性的详细信息选项卡中找到
    5. 尽管我读过另一篇文章,但Thumbprint哈希不区分大小写
    下面是我用于SHA256的命令行:
    signtool.exe sign /sm /debug /v /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 /sha1 65cc2e77dc52b99d46e7caae2377bb5d7d9384b2 "D:\app.msi"
    

    这也解决了我的问题,你必须同时使用 /fd sha256/sha1 <hash>。一开始我觉得这很不直观。 - Lennart

    4
    您可以在项目目录中添加一个批处理文件,例如 sign.bat
    @echo off
    <path>\signtool.exe /f cert.p12 /p "password" "compiled.dll"
    echo Signed with certificate
    

    将该文件添加到你的.gitignore中,但不要将其添加到Visual Studio项目中。
    在项目属性中,将该批处理程序作为后构建事件调用。

    enter image description here


    2
    作为这些技术的新替代品,我创建了一个代码签名服务,个人和组织可以部署到他们的Azure环境中,帮助自动化代码签名,同时确保私钥永远不离开Key Vault的HSM。有关详细信息,请查看项目页面:https://github.com/onovotny/SignService

    0

    当我在VS2015中以"$(SIGNPASS)"的形式提供密码时,从signtool调用对我不起作用,但是如果使用符号"%SIGNPASS%"则可以正常工作。

    检查在VS输出窗口中如何组成完整的signtool命令,命令看起来相同,但在底层有一些差异。

    记得双引号引用路径,以防止路径包含空格。


    $(SIGNPASS) 会被解释为 Visual Studio 宏,您需要将其添加到 props 文件中才能使用它。然后,您可以将该宏设置为环境变量,因为在 props 文件上有一个标志可用于此目的。 - Robson

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接