如何检测已签名C# DLL的文件完整性与真实性?
如何验证已签名C# DLL的文件完整性(不仅仅是证书有效性)
你遇到的问题很典型:当前代码只验证了证书本身的合法性,但没有验证证书对这个DLL的签名是否与文件内容匹配。当你用Resource Hacker修改DLL资源后,文件的哈希值已经改变,但证书本身依然有效,所以Verify()方法自然会返回true——它本来就不负责检查文件是否被篡改。
为什么你的现有代码无法检测篡改?
X509Certificate2.Verify()的作用是验证证书自身的有效性:比如是否在有效期内、是否属于受信任的根证书颁发机构、是否被吊销等。它完全不涉及对DLL文件内容的校验,所以哪怕文件被改得面目全非,只要证书本身没问题,这个方法就会返回true。
正确的解决方案:验证文件签名完整性
要检测文件是否被篡改,你需要验证证书对DLL的签名是否有效——也就是确认当前文件的哈希值和签名时生成的哈希值一致,且签名确实由对应证书签发。最可靠的方式是使用SignedCms类(处理PKCS#7签名,也就是Authenticode签名),它会同时校验签名完整性和证书链有效性。
修改后的代码示例:
public ValidatorResult CheckDll() { try { // 读取完整的DLL文件内容 byte[] fileContent = File.ReadAllBytes(dllFilepath); // 初始化SignedCms来解析文件中的Authenticode签名 var signedCms = new SignedCms(new ContentInfo(fileContent)); signedCms.Decode(fileContent); // 验证签名:参数true表示同时验证证书链的有效性 bool isSignatureValid = signedCms.CheckSignature(true); if (!isSignatureValid) { return new ValidatorResult(ValidatorResult.DllStatus.INVALID_SIGNATURE); } // 可选:进一步自定义证书链验证规则(比如检查吊销状态) X509Certificate2 signingCert = signedCms.SignerInfos[0].Certificate; using (var chain = new X509Chain()) { // 配置链验证策略,比如启用在线吊销检查 chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; // 可以添加自定义信任根等 if (!chain.Build(signingCert)) { return new ValidatorResult(ValidatorResult.DllStatus.INVALID_CERTIFICATE_CHAIN); } } return new ValidatorResult(ValidatorResult.DllStatus.VALID); } catch (CryptographicException) { // 签名解码失败或验证不通过 return new ValidatorResult(ValidatorResult.DllStatus.INVALID_SIGNATURE); } catch (IOException) { // 文件读取失败 return new ValidatorResult(ValidatorResult.DllStatus.FILE_ACCESS_ERROR); } }
代码说明
SignedCms的作用:它会解析DLL中的Authenticode签名,提取出签名时生成的文件哈希,然后和当前文件的哈希做比对,同时验证签名是否由对应的证书签发。CheckSignature(true):这个参数true会触发证书链的验证,确保签名证书是受信任的、未过期且未被吊销。如果只需要验证文件完整性(不关心证书是否受信任),可以传false,但一般建议同时验证证书链。- 自定义链验证:你可以通过
X509ChainPolicy调整验证规则,比如是否允许离线吊销检查、是否添加自定义信任根等,适配你的具体场景。
补充:针对强名称签名的情况
如果你的DLL同时有.NET强名称签名(而非Authenticode数字签名),可以使用StrongNameSignatureVerificationEx方法来验证:
[DllImport("mscoree.dll", CharSet = CharSet.Unicode)] private static extern bool StrongNameSignatureVerificationEx(string wszFilePath, bool fForceVerification, ref bool pfWasVerified); // 使用示例 bool wasVerified = false; bool isStrongNameValid = StrongNameSignatureVerificationEx(dllFilepath, true, ref wasVerified);
但注意:强名称签名主要用于防止篡改和确保版本一致性,它不涉及证书信任链的验证,所以如果是要验证第三方发布的DLL,还是推荐用Authenticode签名的验证方式。
内容的提问来源于stack exchange,提问作者Daniel




