You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Windows下使用C/C++验证PE文件数字签名并获取签名方信息

实现方案

你提到的WinVerifyTrust和CertGetCertificateChain两个API完全适配该场景,前者是Windows资源管理器校验文件签名的底层接口,返回结果和文件属性数字签名标签页的显示完全一致,后者用于解析证书链提取签名主体信息,具体实现逻辑如下:


1. 第一阶段:校验数字签名有效性

如果已经持有文件句柄可以直接传入API,无需重复读取文件;如果只持有内存缓冲区,也可以跳过磁盘读写直接做内存校验。

核心调用逻辑

#include <windows.h>
#include <wintrust.h>
#include <softpub.h>
#pragma comment(lib, "wintrust.lib")

BOOL VerifyFileSignature(HANDLE hFile, LPCWSTR lpFilePath)
{
    // 填充文件信息结构体,已持有文件句柄可直接传入,API会优先用句柄
    WINTRUST_FILE_INFO fileInfo = {0};
    fileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
    fileInfo.hFile = hFile;
    fileInfo.pcwszFilePath = lpFilePath;

    // 填充校验参数
    WINTRUST_DATA trustData = {0};
    trustData.cbStruct = sizeof(WINTRUST_DATA);
    trustData.dwUIChoice = WTD_UI_NONE; // 后台静默校验,不弹出窗口
    trustData.fdwRevocationChecks = WTD_REVOKE_NONE; // 按需开启证书吊销校验
    trustData.dwUnionChoice = WTD_CHOICE_FILE;
    trustData.pFile = &fileInfo;
    trustData.dwStateAction = WTD_STATEACTION_VERIFY;
    trustData.dwProvFlags = WTD_DISABLE_MD2_MD4; // 禁用弱哈希,对齐系统默认策略

    GUID actionGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
    LSTATUS status = WinVerifyTrust(NULL, &actionGuid, &trustData);

    // 校验完成后必须释放状态数据,此处先暂存状态数据用于后续提取证书
    HANDLE hWvtState = trustData.hWVTStateData;
    trustData.dwStateAction = WTD_STATEACTION_CLOSE;
    WinVerifyTrust(NULL, &actionGuid, &trustData);

    // 返回值为ERROR_SUCCESS代表签名有效、文件未被篡改
    return status == ERROR_SUCCESS;
}

内存缓冲区校验适配

如果仅持有文件内容缓冲区,只需将上述代码中WINTRUST_DATA的dwUnionChoice改为WTD_CHOICE_BLOB,填充WINTRUST_BLOB_INFO结构体传入缓冲区地址和长度即可,逻辑完全一致。


2. 第二阶段:提取实际签名主体公司名称

签名校验通过后,从WinVerifyTrust返回的状态数据中提取证书链,证书链的第一个元素就是最底层的实际签名证书(非根CA证书),直接读取其主体信息即可。

核心调用逻辑

注意:该段代码需要放在第一阶段校验成功、释放WVT状态数据之前执行

#include <cryptuiapi.h>
#pragma comment(lib, "crypt32.lib")

BOOL GetSignerCompanyName(HANDLE hWvtState, LPWSTR lpNameBuf, DWORD dwBufSize)
{
    // 从状态数据中获取加密提供者信息
    CRYPT_PROVIDER_DATA* pProvData = WTHelperProvDataFromStateData(hWvtState);
    if (!pProvData || pProvData->csSigners < 1) return FALSE;

    // 取第一个签名的证书链,多签名场景可遍历csSigners字段
    CRYPT_PROVIDER_SGNR* pSigner = &pProvData->pSigners[0];
    if (!pSigner || pSigner->csCertChain < 1) return FALSE;

    // 证书链第一个元素就是实际签名的叶子证书,不是上层CA根证书
    CERT_CONTEXT* pLeafCert = pSigner->pasCertChain[0].pCert;
    if (!pLeafCert) return FALSE;

    // 提取证书主体的组织名称,也可指定OID获取精确字段
    return CertGetNameStringW(pLeafCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, lpNameBuf, dwBufSize) > 1;
}

注意事项

  • 驱动文件和用户态可执行文件的校验逻辑完全一致,无需调整参数
  • 如果需要和系统默认策略完全对齐,可将fdwRevocationChecks改为WTD_REVOKE_WHOLECHAIN开启全链吊销校验,注意会产生网络请求增加耗时
  • 叶子证书的生命周期和WVT状态数据绑定,无需手动释放,释放状态数据时会自动回收

内容的提问来源于stack exchange,提问作者OneAndOnly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 07:54:04