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

C#中如何编程验证调用DLL的EXE文件数字签名

最优实现方案

要实现和SignTool Verify、文件资源管理器数字签名属性页完全一致的校验效果,唯一靠谱的方案是直接P/Invoke调用Windows系统内置的wintrust.dll提供的WinVerifyTrustAPI,不要自己解析PE结构、不要用.NET自带的X509Certificate相关读签名的方法——后者只能提取签名证书,完全不做完整性、信任链、吊销状态校验,结果完全不可信。

这个方案的优势:

  • 和系统原生校验逻辑100%对齐,不存在校验结果和系统显示不一致的问题
  • 无第三方依赖,兼容Windows 7及以上所有主流Windows版本
  • 自动完成签名哈希校验、证书链信任验证、证书吊销检查、时间戳有效性校验所有流程,不需要自己实现复杂的签名逻辑

实现步骤
  • 首先获取当前加载DLL的宿主EXE的完整路径,优先用Process.GetCurrentProcess().MainModule.FileName,比Assembly.GetEntryAssembly().Location兼容性更好,能覆盖非托管宿主加载托管DLL的场景。
  • 定义WinTrust API对应的P/Invoke签名、常量、结构体,注意参数要匹配32/64位系统的结构体内存布局。
  • 调用WinVerifyTrust传入EXE路径,使用系统默认的用户态PE文件签名校验策略,静默执行校验不弹出系统提示框。
  • 根据返回值判断校验结果:返回0代表签名校验通过,非0返回值对应不同的校验失败原因(签名缺失、文件被篡改、证书不受信任、证书被吊销等)。

可直接复用的代码示例
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

public static class ExeSignatureValidator
{
    // WinTrust 常量定义
    private const int WTD_UI_NONE = 2;
    private const int WTD_REVOKE_NONE = 0;
    private const int WTD_CHOICE_FILE = 1;
    private const int WTD_STATEACTION_VERIFY = 0x00000001;
    private const int WTD_STATEACTION_CLOSE = 0x00000002;
    private const int WTD_REVOCATION_CHECK_CHAIN = 0x00000040;
    private static readonly Guid WINTRUST_ACTION_GENERIC_VERIFY_V2 = new Guid(0xaac56b, 0xcd44, 0x11d0, 0x8c, 0xc2, 0x0, 0xc0, 0x4f, 0xc2, 0x95, 0xee);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct WINTRUST_FILE_INFO
    {
        public uint cbStruct;
        public string pcwszFilePath;
        public IntPtr hFile;
        public IntPtr pgKnownSubject;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct WINTRUST_DATA
    {
        public uint cbStruct;
        public IntPtr pPolicyCallbackData;
        public IntPtr pSIPClientData;
        public uint dwUIChoice;
        public uint fdwRevocationChecks;
        public uint dwUnionChoice;
        public IntPtr pUnionData;
        public uint dwStateAction;
        public IntPtr hWVTStateData;
        public string pwszURLReference;
        public uint dwProvFlags;
        public uint dwUIContext;
        public IntPtr pSignatureSettings;
    }

    [DllImport("wintrust.dll", CharSet = CharSet.Unicode, SetLastError = false)]
    private static extern int WinVerifyTrust(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStruct)] Guid pgActionID, ref WINTRUST_DATA pWVTData);

    /// <summary>
    /// 校验当前进程宿主EXE的数字签名,效果和SignTool Verify、资源管理器数字签名页校验一致
    /// </summary>
    /// <returns>true=签名校验通过,false=校验失败</returns>
    public static bool VerifyCurrentHostExeSignature()
    {
        string exePath = Process.GetCurrentProcess().MainModule.FileName;
        return VerifyFileSignature(exePath);
    }

    /// <summary>
    /// 校验指定文件的数字签名
    /// </summary>
    public static bool VerifyFileSignature(string filePath)
    {
        if (!System.IO.File.Exists(filePath))
            return false;

        WINTRUST_FILE_INFO fileInfo = new WINTRUST_FILE_INFO();
        fileInfo.cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_FILE_INFO));
        fileInfo.pcwszFilePath = filePath;
        fileInfo.hFile = IntPtr.Zero;
        fileInfo.pgKnownSubject = IntPtr.Zero;

        IntPtr pFileInfo = IntPtr.Zero;
        WINTRUST_DATA trustData = new WINTRUST_DATA();
        try
        {
            pFileInfo = Marshal.AllocHGlobal(Marshal.SizeOf(fileInfo));
            Marshal.StructureToPtr(fileInfo, pFileInfo, false);

            trustData.cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_DATA));
            trustData.pPolicyCallbackData = IntPtr.Zero;
            trustData.pSIPClientData = IntPtr.Zero;
            trustData.dwUIChoice = WTD_UI_NONE; // 静默校验,不弹任何系统提示
            trustData.fdwRevocationChecks = WTD_REVOKE_NONE;
            trustData.dwUnionChoice = WTD_CHOICE_FILE;
            trustData.pUnionData = pFileInfo;
            trustData.dwStateAction = WTD_STATEACTION_VERIFY;
            trustData.hWVTStateData = IntPtr.Zero;
            trustData.pwszURLReference = null;
            trustData.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN; // 开启证书链吊销检查,和SignTool默认行为一致
            trustData.dwUIContext = 0;
            trustData.pSignatureSettings = IntPtr.Zero;

            int lStatus = WinVerifyTrust(IntPtr.Zero, WINTRUST_ACTION_GENERIC_VERIFY_V2, ref trustData);

            // 校验完成释放状态资源
            trustData.dwStateAction = WTD_STATEACTION_CLOSE;
            trustData.hWVTStateData = (IntPtr)lStatus;
            WinVerifyTrust(IntPtr.Zero, WINTRUST_ACTION_GENERIC_VERIFY_V2, ref trustData);

            return lStatus == 0;
        }
        finally
        {
            if (pFileInfo != IntPtr.Zero)
            {
                Marshal.DestroyStructure(pFileInfo, typeof(WINTRUST_FILE_INFO));
                Marshal.FreeHGlobal(pFileInfo);
            }
        }
    }
}

避坑说明
  • 不要用System.Security.Cryptography.X509Certificates.X509Certificate.CreateFromSignedFile做校验:这个方法只会读取文件中嵌入的签名证书原始数据,完全不会校验文件哈希是否匹配、证书是否受信任、证书是否被吊销,只要文件里带了签名块哪怕是伪造的也会返回证书对象,完全没有安全校验能力。
  • 不要手动解析PE文件的签名目录项:不同Windows版本对签名格式的支持有差异(比如嵌套签名、ECC签名、ED签名的兼容),自己解析很容易出现校验逻辑遗漏,导致签名被绕过,直接用系统API是最安全的选择。
  • 如果需要和SignTool Verify /pa的行为完全对齐,保持代码里用WINTRUST_ACTION_GENERIC_VERIFY_V2这个GUID即可,不要换成驱动签名校验的GUID,否则普通用户态EXE的签名会校验失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 19:21:31