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

C#中DLL方法能否获取当前执行程序集的公钥以验证调用方?

可以通过编程方式从独立DLL中获取调用方已签名程序集的公钥

完全可以实现这个需求,核心思路是获取进程的入口程序集(即启动的可执行文件),提取其公钥并与预先存储的合法公钥对比,以此验证调用方的合法性。

具体实现步骤

1. 获取入口程序集

使用Assembly.GetEntryAssembly()方法可以获取当前进程的启动程序集(也就是调用DLL的可执行文件),这个方法比GetCallingAssembly()更可靠——后者可能返回中间调用层的程序集,而前者直接指向进程的入口点。

2. 提取程序集公钥

从入口程序集的AssemblyName对象中调用GetPublicKey()方法,可获取完整的公钥字节数组。注意不要使用PublicKeyToken(公钥的SHA-1哈希值),虽然它更短,但存在哈希碰撞风险,安全场景下应使用完整公钥。

3. 对比公钥合法性

将提取到的公钥与预先在DLL中存储的合法公钥字节数组进行逐字节对比,完全一致则说明调用方是目标签名程序。

代码示例

using System;
using System.Reflection;

public class CallerValidator
{
    // 预先存储的合法可执行程序的公钥字节数组(需从合法程序中提取)
    private static readonly byte[] ExpectedValidPublicKey = new byte[] 
    { 
        // 替换为你的合法程序公钥字节,例如:
        0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 
        0x06, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x52, 0x53, 0x41, 0x31, 
        0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x79, 0x16, 0x55, 0x69, 
        // ... 剩余公钥字节
    };

    public static bool IsValidCaller()
    {
        // 获取进程入口程序集
        Assembly entryAssembly = Assembly.GetEntryAssembly();
        if (entryAssembly == null)
        {
            // 无法获取入口程序集(如非托管进程调用),直接判定无效
            return false;
        }

        AssemblyName assemblyName = entryAssembly.GetName();
        byte[] callerPublicKey = assemblyName.GetPublicKey();

        // 检查程序集是否已签名(公钥不为空且长度大于0)
        if (callerPublicKey == null || callerPublicKey.Length == 0)
        {
            return false;
        }

        // 逐字节对比公钥
        if (callerPublicKey.Length != ExpectedValidPublicKey.Length)
        {
            return false;
        }

        for (int i = 0; i < callerPublicKey.Length; i++)
        {
            if (callerPublicKey[i] != ExpectedValidPublicKey[i])
            {
                return false;
            }
        }

        // 可选:额外验证程序集签名有效性(防止合法公钥但程序被篡改后重新签名)
        // 需引用System.Security,使用StrongNameSignatureVerificationEx方法
        // bool isSignatureValid = StrongNameSignatureVerificationEx(entryAssembly.Location, true, out bool _);
        // if (!isSignatureValid) return false;

        return true;
    }
}

如何获取合法程序的公钥

可以编写一个简单的工具程序,从已签名的合法可执行文件中提取公钥:

using System;
using System.Linq;
using System.Reflection;

class PublicKeyExtractor
{
    static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.WriteLine("请传入可执行文件路径");
            return;
        }

        Assembly validAssembly = Assembly.LoadFrom(args[0]);
        byte[] publicKey = validAssembly.GetName().GetPublicKey();
        
        // 输出公钥字节数组的C#代码格式
        Console.Write("new byte[] { ");
        Console.Write(string.Join(", ", publicKey.Select(b => $"0x{b:X2}")));
        Console.WriteLine(" };");
    }
}

运行该工具并传入合法exe路径,即可得到可直接复制到DLL中的公钥字节数组代码。

安全注意事项

  • 保护DLL自身安全:建议对DLL也进行强名称签名,并启用系统的强名称验证,防止DLL被篡改。
  • 加密存储公钥:如果担心公钥被逆向提取,可以将公钥加密后存储在DLL中,验证时再解密对比。
  • 处理异常场景:当GetEntryAssembly()返回null(如非托管进程调用DLL)时,直接判定调用方无效,避免后续逻辑出错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 22:35:18