使用signtool.exe为.exe文件签名需先将含私钥的PFX证书导入当前用户个人存储,再执行signtool sign /f "cert.pfx" /p "password" /t "http://timestamp.digicert.com" MyApp.exe;签名后用signtool verify /pa验证,并调WinVerifyTrust确保可信。
怎么用 signtool.exe 给 .exe 文件加数字签名
签名不是 c# 代码里写的,而是构建完成后用 windows sdk 提供的 signtool.exe 工具操作。你得先有 pfx 证书文件(含私钥),且密码已知。
常见错误现象:SignTool Error: No certificates were found that met all the given criteria——多半是证书没导入到当前用户个人证书存储,或 PFX 密码错/证书过期/没启用代码签名用途。
- 确保 PFX 用
certutil -dump your.pfx能正常读出,并看到Enhanced Key Usage: Code Signing - 命令示例:
signtool sign /f "cert.pfx" /p "password" /t "http://timestamp.digicert.com" MyApp.exe;/t时间戳服务器必须加,否则签名随证书过期失效 - 如果用 CI/CD(如 GitHub Actions),注意
signtool需要 Windows 运行时 + Windows SDK,Linux/macOS 上直接跑不了 - 签名后可用
signtool verify /pa MyApp.exe快速检查是否签成功(/pa表示使用 Authenticode 策略验证)
C# 里怎么验证一个 EXE 文件有没有有效签名
不能只看“是否有签名”,得验证签名链可信、未被篡改、且时间在证书有效期内。.NET 没封装完整验证逻辑,得调 Win32 API WinVerifyTrust 或用 System.Security.Cryptography.X509Certificates 手动解析,但后者不校验签名有效性。
最稳的方式是调 WinVerifyTrust,封装成简单方法:
static bool IsSignedAndTrusted(string filePath)
{
var hFile = CreateFile(filePath, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (hFile == IntPtr.Zero) return false;
<pre class='brush:php;toolbar:false;'>var policyGUID = new Guid("a848b716-45e7-488e-b48c-61e03d8d8c61"); // WINTRUST_ACTION_GENERIC_VERIFY_V2
var data = new WINTRUST_DATA { ... }; // 结构体需完整定义(略)
var result = WinVerifyTrust(IntPtr.Zero, ref policyGUID, ref data);
CloseHandle(hFile);
return result == 0;}
容易踩的坑:WinVerifyTrust 返回 0 表示可信,不是“成功”;返回 0x800b0101(CERT_E_EXPIRED)或 0x800b0109(CERT_E_UNTRUSTEDROOT)说明验证失败,但程序不会抛异常,得自己查错误码。
- 别用
Assembly.GetExecutingAssembly().GetName().GetPublicKey()判断——它只读强名称,和 Authenticode 签名完全无关 - 验证前确保目标文件没被锁定(比如正在运行),否则
CreateFile失败 - 若在沙盒环境(如 ClickOnce 或某些杀软拦截下),
WinVerifyTrust可能返回0x80096010(TRUST_E_NOSIGNATURE),不是证书问题,是访问受限
为什么签名了还是被 Windows SmartScreen 拦截
签名只是第一步,SmartScreen 看的是“这个发布者是否被用户/系统信任”,核心是声誉积累,不是技术动作做完就自动放行。
典型现象:新证书签名的 EXE 第一次下载,提示“未知发布者”,点“更多信息”→“仍要运行”,之后才可能逐步降低警告级别。
- 单次签名无法绕过 SmartScreen,哪怕用了 DigiCert/OV 证书也一样;EV 证书能跳过首次警告,但需硬件令牌+人工审核,成本高
- 分发渠道影响极大:从你自己的 HTTPS 域名下载,比从百度网盘/邮件附件下载更容易建立信誉
- 文件名别带“crack”“patch”“keygen”等敏感词,SmartScreen 会直接关联恶意行为模式
- 提交到 Microsoft SmartScreen Submission Portal(
https://smartscreen.microsoft.com)可加速信誉收录,但需提供公司信息和文件哈希
签名相关路径和配置项容易填错的地方
路径和参数名看着简单,拼错一个字符就失败,而且错误提示往往不明确。
-
signtool.exe路径常在C:\Program Files (x86)\Windows Kits\10\bin\<ver>\x64\signtool.exe,不同 SDK 版本号(如10.0.22621.0)要核对清楚,别用旧版去签 Win11 兼容文件 - 时间戳 URL 必须用 HTTP(不是 HTTPS),且服务要在线;
http://timestamp.digicert.com和http://timestamp.sectigo.com是目前较稳的两个 - PFX 文件路径含空格必须用英文双引号包裹,但密码参数
/p后面的值**不能加引号**,否则当成带引号的密码处理 - 验证时用的
WINTRUST_DATA结构里,dwUIChoice设成WTD_UI_NONE,否则弹窗打断自动化流程
真正卡住人的,往往是证书导出时没勾选“导出私钥”,或者 PFX 密码输错三次导致 Windows 证书存储锁死——这时候重装证书都无效,得进 certmgr.msc 手动删掉冲突项再重导。










