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
相关产品推荐
相关产品推荐

