由于私钥过滤器,使用 signtool 进行代码签名失败

Code signing with signtool fails due to private key filter

在尝试签署由我工作的公司创建的某些安装程序时,我遇到了一个错误,我无法解决。我正在使用已在另一台机器(Win7)上以相同方式成功使用的相同证书来签署准相同的安装程序。无论如何,在我们的 Windows Server 2008 即 运行 CruiseControl.net 上,我尝试使用 signtool.exe 签署安装程序,但失败并出现以下错误:

The following certificates were considered:
    Issued to: <our company>
    Issued by: <some ca>
    Expires:   <is valid>
    SHA1 hash: <...>

    Issued to: <...>
    Issued by: <...>
    Expires:   <...>
    SHA1 hash: <...>

After EKU filter, 1 certs were left.
After expiry filter, 1 certs were left.
After Subject Name filter, 1 certs were left.
After Private Key filter, 0 certs were left.
SignTool Error: No certificates were found that met all the given criteria.

我尝试将证书安装到不同的证书存储,尝试不同版本的 signtool.exe 并尝试直接使用 .cer 文件,但没有任何区别。在所有情况下,我都会收到上述错误。我尝试了以下命令行命令

signtool.exe sign /debug /n "MyCompany" C:\my\installer.exe
signtool.exe sign /debug /f C:\path\to\my\certificate.cer C:\my\installer.exe

但在某些情况下我没有使用 /debug。我做错了什么或遗漏了什么吗?

为了签署文件,您需要拥有证书的私钥,它不包含在您从 Windows 7 机器复制的 *.cer 文件中。要导出带有私钥的证书,您可以按照 instructions supplied here.

请注意,如果证书在创建时设置为允许导出(通过将 -pe 传递给 makecert),您将只能导出私钥

我有相同的症状,但完全不同的原因。正如许多开发人员所做的那样,我的系统上安装了一堆不同的工具链。我只是对他们进行了调查,以展示这看起来如何;滚动到此答案的底部以获取完整列表。

我像往常一样从提升的命令提示符下使用 certutil -importPFX cert.pfx 将我的代码签名证书从 VeriSign 安装到系统证书存储(需要 /smsigntool.exe)。

最初的测试看起来很有希望,但突然间签名开始失败。

为了调试问题,我首先开始使用 signtool.exe sign /debug /v /a /sm ... 以查看问题出在哪里。输出看起来像这样(另见问题):

The following certificates were considered:
    Issued to: localhost
    Issued by: localhost
    Expires:   Tue Dec 26 00:00:00 2017
    SHA1 hash: <...>

    Issued to: <...>
    Issued by: Symantec Class 3 SHA256 Code Signing CA
    Expires:   <...>
    SHA1 hash: <...>

After EKU filter, 1 certs were left.
After expiry filter, 1 certs were left.
After Root Name filter, 1 certs were left.
After Private Key filter, 0 certs were left.
SignTool Error: No certificates were found that met all the given criteria.

我可以排除丢失私钥的可能性,因为证书存储区清楚地表明我有一个匹配私钥:

现在我记得有 some recent patches 允许 Windows 7 接受使用具有 SHA256 哈希的证书制作的签名。当然,尽管大多数较旧的文章会指出 Windows 7 根本无法处理 SHA-2 哈希。

所以这已经让我朝着 "it's got to be an old version of something involved in signing" 的方向前进了。

仍然决定删除证书和密钥并使用之前显示的调用重新导入它。

然后,在调查我的系统后(见答案底部),我发现 五个 个不同版本的 signtool.exe。所以我开始尝试最新的(6.3.9600.17298,来自Windows 8.1 SDK)并且它立即工作

signtool.exe sign /debug /v /a /sm /r VeriSign /ac MSCV-VSClass3.cer /ph  /t "http://timestamp.verisign.com/scripts/timstamp.dll" *.exe

The following certificates were considered:
    Issued to: localhost
    Issued by: localhost
    Expires:   Tue Dec 26 00:00:00 2017
    SHA1 hash: <...>

    Issued to: <...>
    Issued by: Symantec Class 3 SHA256 Code Signing CA
    Expires:   <...>
    SHA1 hash: <...>

After EKU filter, 1 certs were left.
After expiry filter, 1 certs were left.
After Root Name filter, 1 certs were left.
After Private Key filter, 1 certs were left.
The following certificate was selected:
    Issued to: <...>
    Issued by: Symantec Class 3 SHA256 Code Signing CA
    Expires:   <...>
    SHA1 hash: <...>

Cross certificate chain (using machine store):
    Issued to: Microsoft Code Verification Root
    Issued by: Microsoft Code Verification Root
    Expires:   Sat Nov 01 13:54:03 2025
    SHA1 hash: 8FBE4D070EF8AB1BCCAF2A9D5CCAE7282A2C66B3

        Issued to: VeriSign Class 3 Public Primary Certification Authority - G5
        Issued by: Microsoft Code Verification Root
        Expires:   Mon Feb 22 19:35:17 2021
        SHA1 hash: 57534CCC33914C41F70E2CBB2103A1DB18817D8B

            Issued to: Symantec Class 3 SHA256 Code Signing CA
            Issued by: VeriSign Class 3 Public Primary Certification Authority - G5
            Expires:   Sat Dec 09 23:59:59 2023
            SHA1 hash: 007790F6561DAD89B0BCD85585762495E358F8A5

                Issued to: <...>
                Issued by: Symantec Class 3 SHA256 Code Signing CA
                Expires:   <...>
                SHA1 hash: <...>


The following additional certificates will be attached:
    Issued to: VeriSign Class 3 Public Primary Certification Authority - G5
    Issued by: Microsoft Code Verification Root
    Expires:   Mon Feb 22 19:35:17 2021
    SHA1 hash: 57534CCC33914C41F70E2CBB2103A1DB18817D8B

    Issued to: Symantec Class 3 SHA256 Code Signing CA
    Issued by: VeriSign Class 3 Public Primary Certification Authority - G5
    Expires:   Sat Dec 09 23:59:59 2023
    SHA1 hash: 007790F6561DAD89B0BCD85585762495E358F8A5

Done Adding Additional Store
Successfully signed: <...>.exe

Number of files successfully Signed: 1
Number of warnings: 0
Number of errors: 0

进一步追踪,我认为我已经找到了问题所在。然而,事实证明我得到的错误不是我在旧 signtool.exe 版本中看到的错误。相反,旧版本会分别抱怨 /ac/fd/ph 是无法识别的命令行选项。

所以我需要更深入地挖掘,结果证明我的(替代)文件管理器是罪魁祸首。我通常使用该文件管理器和方便的键盘快捷键在相应的文件夹中启动命令提示符。事实证明,它有时不会传递环境变量——本质上是文件管理器 "forgets" 环境变量。事实证明这是根本原因。使用 Win+R 打开命令提示符然后 cmd Enter 不会暴露这个尽管从同一文件夹执行 signtool.exe 仍存在行为。

我最好的猜测是,由于 PATH 变量或类似的混乱,signtool.exe 最终选择了错误的 DLL。值得注意的是 mssign32.dllwintrust.dll 伴随 signtool.exe 在 Windows SDK 8.0 和 8.1 的同一文件夹中,但不是任何signtool.exe 的早期版本将选择 "global" 系统范围的 DLL,无论它们是什么。


在我的系统上,我有 五个 个不同版本的 signtool.exe

signtool.exe 5.2.3790.1830

甚至不理解我使用的 /ac/ph 参数(也不理解 /fd)。但奇怪的是,没有这两个参数也能工作。

  • C:\Program Files (x86)\Microsoft Visual Studio 8\SDK\v2.0\Bin\signtool.exe

signtool.exe6.0.4002.0

甚至不理解我使用的 /ac/ph 参数(也不理解 /fd)。但奇怪的是,没有这两个参数也能工作。

  • C:\Program Files (x86)\Microsoft Visual Studio 8\Common7\Tools\Bin\signtool.exe

signtool.exe6.1.7600.16385

先看版本/fd sha256.

  • C:\WINDDK00.16385.1\bin\amd64\SignTool.exe
  • C:\WINDDK00.16385.1\bin\x86\SignTool.exe
  • C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\signtool.exe
  • C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\signtool.exe
  • C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin\signtool.exe

signtool.exe6.2.9200.20789

  • C:\Program Files (x86)\Windows Kits.0\bin\x64\signtool.exe
  • C:\Program Files (x86)\Windows Kits.0\bin\x86\signtool.exe

signtool.exe6.3.9600.17298

  • C:\Program Files (x86)\Windows Kits.1\bin\arm\signtool.exe
  • C:\Program Files (x86)\Windows Kits.1\bin\x64\signtool.exe
  • C:\Program Files (x86)\Windows Kits.1\bin\x86\signtool.exe

我在 Win7 机器上遇到了同样的问题,并尝试了好心人在此 post 中提出的所有建议,但没有成功。然后,即使我的机器只有一个帐户并且具有管理员权限,我通过 "Run as administrator" 打开命令提示符 window 然后我安装的所有版本 signtool.exe 开始工作。

我还必须使用从其他来源(类似于您)收到的证书对文件进行签名。对我来说,问题是我只使用 "Current User" 选项在我的 PC 上安装了证书。一旦我使用 "Local Machine" 选项安装它,它就起作用了。

我遇到了与上述相同的问题。这就是我所做的 - 假设您已经为代码签名设置了 pre-requisites 的其余部分。

  1. 关闭解决方案并关闭VSIDE
  2. 以 运行 作为管理员权限启动了 Visual Studio IDE
  3. 打开解决方案并重建它

这次签名过程成功。

我假设您有正确的证书文件和用于签名的私钥,并且还将证书导入到受信任的存储区。

如果没有,您可以继续阅读更多内容。

  1. 导入 pfx 文件 - "xyz.pfx" :: 打开命令提示符(启动 - 运行 作为管理员权限)和 运行 以下命令

    certutil.exe -p mypassword -importpfx xyz.pfx

    命令的输出如下所示:

    Certificate "CN=Name of the Cert, O=BLAH, OU=BLAH, E=john.doe@yourorg.com" added to store. CertUtil: -importPFX command completed successfully.

  2. 使用以下命令将证书添加到受信任的根证书颁发机构。有很多方法可以做到,但我使用了这个。打开命令提示符(启动 - 运行 以管理员权限)和 运行 以下命令

    certmgr.exe -add -c mycertificate.cer -s -r localMachine root

    您将看到这样的输出消息

    CertMgr Succeeded

  3. 在程序集上签名。来自 Visual Studio IDE.

    的简单过程
    • 右键单击解决方案中的项目并打开其属性。
    • 打开 Signing 部分并单击 Sign the Assembly 复选框(启用它)
    • 使用浏览选项选择 pfx 文件并输入密码
      you can also create a new one but you need a cert file that match to this pfx file, otherwise your signing will fail.
    • 保存更改
    • 现在,单击 Build Events 部分并单击 Edit Post-Build 打开编辑器post 构建事件。输入以下命令。

      "C:\Program Files (x86)\Windows Kits\bin.0.18362.0\x64\signtool.exe" sign /v /sm "$(TargetPath)"

      Path of the signtool.exe depends on where/whether you have installed the Windows SDK.

现在构建解决方案,您的解决方案应该成功构建并且您的二进制文件已签名。确保 运行 VS IDE 具有管理员权限。

只是为了确定...
您需要一个特殊的“代码签名”证书才能对您的代码进行签名...
我已经浪费了几个小时试图用我们的域证书签署我们的应用程序,这是不可能的。