当前位置: 首页 > news >正文

Windows 数字签名获取与验证详解

在 Windows 的安全体系中,数字签名扮演着“软件身份证”的角色。它可以证明一个程序确实来自某个发布者,并且在分发的过程中没有被篡改。

当下载一个系统更新、驱动程序,或者安装第三方应用时,操作系统往往会验证数字签名,确保软件来源合法、安全。那么,作为开发者或安全研究人员,该如何编程获取并验证这些数字签名呢?

本文将通过代码示例,逐步实现一个数字签名验证工具,并解析其原理和应用场景。

为什么要验证数字签名?

在信息安全领域,数字签名主要解决两个问题:

  1. 来源认证
    通过证书绑定发布者的身份,确认文件确实来自于声称的公司或组织。比如系统文件通常签名于 Microsoft Corporation
  2. 完整性保护
    签名过程包含哈希计算,如果文件在传输中被篡改,签名将立刻失效。

对普通用户来说,这能防止中途被植入恶意代码;对企业来说,则能确保生产环境中运行的软件是可信的。

验证思路与 Windows API

Windows 提供了专门的 API 来处理数字签名验证,其中最关键的包括:

  • WinVerifyTrust:验证文件的数字签名是否有效;
  • WTHelperProvDataFromStateData:获取验证过程中的证书链数据;
  • WTHelperGetProvSignerFromChain:提取签名者、时间戳签名者等信息;
  • CertGetNameString:获取证书的持有人或颁发者名称;
  • CryptHashCertificate2:计算证书的 SHA1 指纹。

我们的目标是用这些 API 编写一个签名验证器,它不仅要能告诉我们签名是否有效,还能展示签名的详细信息。

定义数据结构:存储验证结果

首先,需要定义一个结构体,用来存储每次验证得到的签名信息。

struct SignatureInfo {std::string status;        // 验证状态简写(如 Valid、Expired)std::string statusMessage; // 状态详细描述std::string thumbprint;    // 证书指纹(SHA1)std::string signer;        // 签名者std::string issuer;        // 颁发者std::string timeStamper;   // 时间戳服务器(如果存在)bool isValid = false;      // 签名是否有效
};

通过这样的结构体,就能清晰地记录和返回验证结果,方便展示和后续处理。

调用 WinVerifyTrust 验证签名

验证的第一步就是调用 WinVerifyTrust,系统会说明这个文件的签名是否可信。

WINTRUST_FILE_INFO fileData = { sizeof(WINTRUST_FILE_INFO) };
fileData.pcwszFilePath = filePath;WINTRUST_DATA wintrustData = { sizeof(WINTRUST_DATA) };
wintrustData.dwUIChoice = WTD_UI_NONE;                 // 不弹出 UI
wintrustData.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN; // 检查整个证书链
wintrustData.dwUnionChoice = WTD_CHOICE_FILE;
wintrustData.pFile = &fileData;
wintrustData.dwStateAction = WTD_STATEACTION_VERIFY;GUID action = WINTRUST_ACTION_GENERIC_VERIFY_V2;
LONG result = WinVerifyTrust(NULL, &action, &wintrustData);

通过返回值 result 可以验证是否成功:

  • ERROR_SUCCESS:签名有效;
  • 其他值:签名无效,需要进一步分析原因,比如证书过期、吊销、不受信任等。

这相当于签名验证的入口,后续的步骤都建立在这之上。

提取签名证书信息

如果验证成功,便可以提取证书链的详细信息,包括:

  • 签名者(Signer):谁签了这个文件;
  • 颁发者(Issuer):证书由谁颁发;
  • 证书指纹(Thumbprint):用于唯一标识证书;
  • 时间戳(Timestamp):签名时刻的时间戳,确保即使证书后来过期,签名依然有效。

核心代码如下:

CRYPT_PROVIDER_DATA* providerData = WTHelperProvDataFromStateData(wintrustData.hWVTStateData);
if (providerData) {CRYPT_PROVIDER_SGNR* signer = WTHelperGetProvSignerFromChain(providerData, 0, FALSE, 0);if (signer && signer->pasCertChain[0].pCert) {PCCERT_CONTEXT certContext = signer->pasCertChain[0].pCert;info.thumbprint = GetCertificateThumbprint(certContext);info.signer = GetCertificateName(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE);info.issuer = GetCertificateName(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE | CERT_NAME_ISSUER_FLAG);// 获取时间戳(如果存在)if (signer->csCounterSigners > 0) {CRYPT_PROVIDER_SGNR* timeStamper = WTHelperGetProvSignerFromChain(providerData, 0, TRUE, 0);if (timeStamper && timeStamper->pasCertChain[0].pCert) {info.timeStamper = GetCertificateName(timeStamper->pasCertChain[0].pCert, CERT_NAME_SIMPLE_DISPLAY_TYPE);}}}
}

通过证书链,可以把签名的每一个关键细节抽取出来。

辅助函数:让结果更直观

仅仅有 API 返回的原始数据并不友好,还需要写几个辅助函数,便于转化结果。

获取证书指纹

证书指纹是证书的 SHA1 哈希,常用于唯一标识:

static std::string GetCertificateThumbprint(PCCERT_CONTEXT certContext) {BYTE thumbprint[20] = {0};DWORD thumbprintSize = sizeof(thumbprint);if (CryptHashCertificate2(BCRYPT_SHA1_ALGORITHM, 0, NULL, certContext->pbCertEncoded, certContext->cbCertEncoded, thumbprint, &thumbprintSize)) {std::stringstream ss;for (DWORD i = 0; i < thumbprintSize; i++) {ss << std::hex << std::setw(2) << std::setfill('0') << (int)thumbprint[i];}return ss.str();}return "";
}

获取证书名称

证书包含了签名者和颁发者的名称,我们用 CertGetNameString 提取:

static std::string GetCertificateName(PCCERT_CONTEXT certContext, DWORD type) {DWORD size = CertGetNameStringA(certContext, type, 0, NULL, NULL, 0);if (size > 1) {std::vector<char> buffer(size);CertGetNameStringA(certContext, type, 0, NULL, buffer.data(), size);return buffer.data();}return "";
}

状态码映射

不同的错误码代表不同的签名状态,可以映射为更易懂的文字:

static std::string GetStatusText(LONG status) {switch (status) {case ERROR_SUCCESS: return "Valid";case TRUST_E_NOSIGNATURE: return "NotSigned";case CERT_E_EXPIRED: return "Expired";case CERT_E_REVOKED: return "Revoked";case TRUST_E_EXPLICIT_DISTRUST: return "Distrusted";case TRUST_E_BAD_DIGEST: return "Invalid";case CERT_E_UNTRUSTEDROOT: return "UntrustedRoot";default: return "Error";}
}

测试与运行结果

最后,可以这样使用:

int main() {auto info = SignatureVerifier::GetSignatureInfo(L"C:\\Windows\\explorer.exe");printf("Status: %s\n", info.status.c_str());printf("Message: %s\n", info.statusMessage.c_str());printf("Thumbprint: %s\n", info.thumbprint.c_str());printf("Signer: %s\n", info.signer.c_str());printf("Issuer: %s\n", info.issuer.c_str());printf("TimeStamper: %s\n", info.timeStamper.c_str());printf("IsValid: %s\n", info.isValid ? "Yes" : "No");
}

运行结果:

Status: Valid
Message: Signature verified
Thumbprint: 3b77d.........
Signer: Microsoft Windows
Issuer: Microsoft Windows
TimeStamper: Microsoft Time-Stamp Service
IsValid: Yes

这表明 explorer.exe 的签名有效,签名者是微软,签名带有可信时间戳。

应用场景

签名验证器不仅仅是一个演示工具,它在实际工作中有很多应用:

  • 企业安全管理:批量检查公司内部运行的软件,确保没有未签名或不明来源的程序。
  • 安全软件开发:防病毒软件、白名单系统常常依赖数字签名来判断文件的可信度。
  • CI/CD 发布流程:在软件发布前自动验证签名,避免被篡改的文件进入生产环境。

在实际应用中,除了数字签名验证以外,借助专业的加固工具也能进一步增强软件的防护效果。例如 Virbox Protector 就是一款专注于 Native 层安全的商业级解决方案,能够在抵御调试和逆向方面提供额外保障,为软件供应链安全增加一道坚实的屏障。

总结

数字签名是验证软件来源和完整性的重要手段。通过调用 Windows 提供的 API,可以有效地检查文件签名的有效性,并获取证书的详细信息。无论是系统安全防护、软件发布流程,还是日常文件校验,合理使用签名验证机制都能提升整体的安全性和可信度。

http://www.wxhsa.cn/company.asp?id=7678

相关文章:

  • 转化率提升300%,火山引擎Data Agent以“一客一策”突破企业营销增长瓶颈
  • 矩阵模板
  • 快读模板
  • ipadװwindowsϵͳshell
  • cpu的各种寄存器及其功能
  • 如何关闭电视的ACR功能及其对隐私保护的重大意义
  • huggingface 模型权重文件
  • vscode设置单击选中带连字符的单词
  • P4147 玉蟾宫(悬线法)
  • 全局平衡二叉树
  • Transactional注解的方法里 spring怎么知道我用的是哪个jdbctemplate实例
  • 根据参数查询
  • 关于非侵入式脑机接口面向C端一个应用想法
  • Blelloch并行扫描算法
  • 国产化DevOps生态崛起:Gitee如何赋能企业数字化转型
  • 【IEEE出版】2025年电气、控制与人工智能国际学术会议(ICOECAI 2025)
  • 采购计划 vs 物料需求计划(MRP),采购新手最容易搞混的两份“清单”!
  • P10299 [CCC 2024 S5] Chocolate Bar Partition
  • 实用指南:企业实施数字化转型时常见的挑战
  • 当ARMxy+AI边缘计算落地水泵行业就碰撞出怎样的火花?
  • QN8035 FM芯片驱动开发
  • 再见 Claude Code,我选择了 Codex!真香!!
  • 2025中国DevOps工具生态全景:本土化突围与智能化跃迁
  • 字符串转 python 对象 eval
  • 蛋白多序列比对美化
  • Gitee推出Remote mcp-gitee:云端MCP服务开启智能协作新时代
  • Gitee DevOps平台:驱动中国企业数字化转型的核心引擎
  • 10 类多布局扫描图像数据集:支撑 OCR 精度提升与 VLM 微调,覆盖广告 / 简历 / 论文等场景的计算机视觉训练数据
  • 国产化Excel开发组件Spire.XLS教程:C# 轻松将 DataSet 导出到 Excel
  • Mysql:Docker的Mysql容器加载Levenshtein 距离算法脚本,实现“相似度匹配”