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

Posted

技术标签:

【中文标题】使用 signtool.exe 自动进行代码签名,但不存储证书或密码【英文标题】:Automating code signing with signtool.exe, but without storing the certificate or password 【发布时间】:2014-09-29 16:47:53 【问题描述】:

我在 Visual Studio 2013 中有一个 C#/.NET 4.5 x64 项目。不止一个开发人员在此项目上工作,因此代码在 Git 中进行管理。我正在用signtool.exe 签署已编译的.dlls 和.exe。我的公司购买了代码签名证书,如果我像这样从命令行手动签名:

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

...然后看起来一切都很好:我收到一条成功消息,并且 Windows 资源管理器中已编译 DLL 的属性显示它已正确签名。所以,我对实际的签名过程没有任何问题。

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

所以,这是我的问题:我如何配置 Visual Studio 2013 以自动签署其编译的输出,而不在 Git 中保留证书或密码?我希望它尽可能简单,一旦开发人员在预定义的路径(或导入的,或其他)中拥有证书,并假设开发人员知道证书的密码,在 Visual Studio 2013 中单击“构建”只是构建并签署它,没有问题。

如果签名过程可以是非交互式的(没有密码提示),那是一个好处。最终,这将成为持续集成 (CI) 服务器的一部分,该服务器也可以对其输出进行签名,并且由于它是自动化的,因此没有人会在那里输入密码。不过,我现在会采取任何解决方案。

我的证书是 PKCS #12 格式并受密码保护。 Windows 声称它没有被标记为导出。

【问题讨论】:

【参考方案1】:

我之前使用过的解决方案类似于@Mikko 的回答,但它分为两部分:

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

    @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 步骤中作为脚本参数传递的可执行文件的路径。 ..

【讨论】:

我没想过这样拆分它;我有关于使用文本文件的想法,但环境变量似乎要好得多。谢谢;我明天试试这个。 您在 signtool 路径后缺少“sign”命令【参考方案2】:

另一种方法是在每个开发人员私有证书存储中导入证书,然后将指纹与 signtool 一起使用,如下所示:

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

那么您只需要在证书的初始导入期间而不是在构建期间需要密码。

【讨论】:

这种方法效果很好,解决了我在代码签名中遇到的几个安全问题。现在我的项目不需要证书或密码。我只是将它安装在证书存储中并在任何地方使用指纹。感谢您的提示!非常好的建议。【参考方案3】:

我无法补充 Daniel Schlößer 的答案——在我看来,这确实是最好的答案。将证书添加到证书存储后,就不需要密码和 pfx 进行签名。

但是我想添加以下 cmets:

    证书必须正确安装在证书存储中(我使用了 .pfx 和密码) signtool.exe 必须支持 /sha1 命令行选项(我使用的是 10.0.16299) 对于我的证书,我花了很长时间才意识到我需要使用 /sm 开关来指定“机器存储”而不是“用户存储” 指纹位于证书属性的详细信息选项卡中 尽管我阅读了另一篇文章,但指纹哈希不区分大小写

所以这是适用于 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>。一开始我觉得这很不直观。【参考方案4】:

您可以在项目目录中添加一个batch 文件,例如sign.bat

@echo off
<path>\signtool.exe /f cert.p12 /p "password" "compiled.dll"
echo Signed with certificate

将文件添加到您的.gitignore,但不要将其添加到 Visual Studio 项目中。

在您项目的属性中,调用 batch 作为构建后事件。

【讨论】:

【参考方案5】:

作为这些的更新替代方案,我创建了一个代码签名服务,个人和组织可以将其部署到他们的 Azure 环境中,它可以帮助自动化代码签名,同时确保私钥永远不会离开 Key Vault 的 HSM。

详情请看这里的项目页面:https://github.com/onovotny/SignService

【讨论】:

【参考方案6】:

当我以 "$(SIGNPASS)" 的形式提供密码时,从 VS2015 调用 signtool 对我不起作用,但当您使用符号 "%SIGNPASS%" 时它确实有效。

检查完整的 signtool 命令是如何在 VS 输出窗口中形成的,这些命令看起来是一样的,但实际上有一些不同。

记得给路径加上双引号,以保护自己免受包含空格的路径的影响。

【讨论】:

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

以上是关于使用 signtool.exe 自动进行代码签名,但不存储证书或密码的主要内容,如果未能解决你的问题,请参考以下文章

使用 .cer 文件对 .exe 进行签名(signtool.exe 要求的证书名称是啥?)

微软代码签名证书使用指南(SignTool)

Windows 代码签名过程和 MS signtool.exe 的替代方案? [关闭]

signtool 无法使用时间戳对 SHA2 和 SHA1 进行双重签名

自动化扩展验证 (EV) 代码签名

Visual Studio 签名失败(signtool.exe 错误)