浅谈.NET程序集安全签名

简介: 本文讲的是浅谈.NET程序集安全签名,如果你能看懂上面的代码行是什么意思,那么你可能是一个.NET开发人员。同时你也可能知道结尾处的十六进制字符串表示的是一个公共密钥令牌,这是一个表示程序集具有强名称签名的标识。
本文讲的是 浅谈.NET程序集安全签名

TestLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=769a8f10a7f072b4

如果你能看懂上面的代码行是什么意思,那么你可能是一个.NET开发人员。同时你也可能知道结尾处的十六进制字符串表示的是一个公共密钥令牌,这是一个表示程序集具有强名称签名的标识。 但你知道如何计算这个令牌吗? 或者你知道强名称签名的结构吗? 在这篇文章中,我将详细介绍强名称的工作原理及其缺点。 我们还将看看基于证书的签名,最后,我们将检查程序集验证签名的过程。

强名称和公共密钥令牌

一个有效的强名称签名可以确保收件人收到的程序集没有被篡改。同时,它还可以作为给定的程序集的唯一地标识。虽然,它没有说明关于签名者身份的任何信息。有两部分程序集二进制代码在强名称签名验证过程中发挥着作用。

第一部分是公共密钥,它是程序集元数据中的#Blob流的一部分(下面的屏幕截图中显示了dnSpy窗口的一部分):

浅谈.NET程序集安全签名

下图中列出了构建公钥块的元素:

浅谈.NET程序集安全签名

浅谈.NET程序集安全签名

用于唯一地引用程序集的公共密钥令牌是以公共密钥的SHA-1哈希的低8字节的十六进制并以反向字节顺序表示的。对于我本文中测试使用的程序集来说,公共密钥令牌等于:769a8f10a7f072b4(SHA-1(00 24 00 00 0c 80 … c8 8a c1 b1)= 9aa4de0a96ada8d83d6d7678b472f0a7108f9a76)。

第二部分是程序集内容哈希后的RSA签名。 在计算这个哈希值之前,我们需要用零填充文件的以下字节:认证签名条目(我们稍后会得到它),强名称块和PE头校验和。 之后,签名会存储在PE文件的text节中,其文件地址为保存在COR20头中的偏移量:

浅谈.NET程序集安全签名

为了计算RSA签名,我们需要拥有与上一张图片中列出的公共密钥相对应的私钥。 如果你想看看实现验证的C#代码,你可以查看dnLib库中的StrongNameSigner.cs文件。

在研究了强名称签名结构之后,现在让我们关注我们可以用来创建强名称签名结构的工具。 我们从生成.snk文件开始,该文件将存储RSA密钥的详细信息(如果你对.snk文件格式的详细信息感兴趣,请查看我的010编辑器模板):

sn.exe -k 2048 TestLib.snk

我们应该保存好生成的.snk文件的密文。 接下来,根据我们的方案,我们可以使用C#编译器(csc.exe)或程序集链接器(al.exe)。 这两者都接受 /keyfile参数,我们为这个参数提供了我们刚才生成的.snk文件的路径,例如。

csc.exe /keyfile:TestLib.snk /t:library TestLib.cs

此命令将基于文件内容的SHA-1的哈希值生成签名。 现在,SHA-1被认为不足以进行安全哈希操作,强烈建议使用SHA-2摘要算法。 要使用SHA-2哈希来对我们的程序集签名,我们首先需要提取.snk文件的公钥部分:

sn.exe -p TestLib.snk TestLibPubKey.snk sha256

然后延迟私钥签名过程(强名称签名块将被填充为零,强名称签名标志不会被置位)。 csc.exe和al.exe都接受 /delaysign+参数:

csc.exe /keyfile:TestLibPubKey.snk /delaysign+ /t:library TestLib.cs

最后,我们需要使用私钥重新签名程序集:

sn -Ra TestLib.dll TestLib.snk

如果你有一个强名称的程序集,并希望迁移签名,请看看这篇文章

验证码签名

验证码签名(Autheticode signature,),顾名思义,是用于验证程序集的所有者。它还用于保护程序集的完整性。签名的大小和位置存储在PE可选头中:

浅谈.NET程序集安全签名

Force Integrity标志(我在图中已经标记了),用于强制加载器始终检查给定程序集的签名(针对加载受保护的进程的驱动程序和模块,Windows会跳过签名验证)。 对于本地代码,有一个特殊的链接器选项来启用此特性。 我没有在csc.exe或al.exe中找到这样的参数选项以及使用dnSpy来设置这个标志位(我需要首先延迟签名程序集的过程,设置此标志,并重新进行签名)。

要创建验证码,我们需要拥有包含私钥的证书(需要.pfx格式)。 为了测试目的,自签名证书也是有效的(除非你设置了强制完整性的标志),但对于发布部署的情况,你最好应该从受信任的提供者那里获取一个证书。 使用证书文件对程序集签名的命令示例如下所示:

signtool sign /v /ph /fd sha256 `
/f .fileSignature.pfx `
/p {certificate-password} `
/t http://timestamp.verisign.com/scripts/timstamp.dll .TestLib.dll

记住在创建验证码之前创建强名称签名。

签名验证

.NET从3.5版本开始,在程序集被加载到一个完全信任的应用程序域时不会执行强名称签名验证。这基本上意味着在完全信任的应用程序域中,我们可以用一个只包含公钥的程序集来替换一个具有强名称的程序集,并且没有人会注意到程序集被替换。我们可以通过在配置文件中启用运行时的bypassTrustedAppStrongNames属性,或者通过将HKLMSOFTWAREMicrosoft.NETFramework键中的AllowStrongNameBypass值设置为零(在64位系统上可以使用Wow6432作为32位应用程序)来更改此行为。

即使已经有以上这些设置选项保证程序集不被替换,但仍然有一种方法可以将部分签名的程序集加载到我们的应用程序域中。在延迟程序集签名过程时,可以使用此绕过机制。在开发期间,我们通常不想在每个构建的程序中对程序集进行完全签名(私钥放在在一个地方才能保持安全性),但同时,我们又希望程序集具有强名称的行为。这可以通过在HKLMSoftwareMicrosoftStrongNameVerification下的注册表中添加我们的程序集名称和公共密钥令牌来实现,例如:HKLMSoftwareMicrosoftStrongNameVerificationTestLib,8FCE6031CC56162D,或使用sn命令的-Vr选项。注:只有系统管理员可以修改验证列表。

当涉及Authenticode验证时,.NET仅在PE头中的强制完整性标志被设置时才会执行验证。

幸运的是,我们不需要依赖自动验证;我们可以自己执行验证。我们用来对程序集签名的相同工具,为我们提供了验证它们的方法。要检查强名称签名,我们可以使用sn -vf(-f选项强制 sn检查签名,即使它在注册表中已被禁用)。将密钥文件作为运行参数是可选的,但是建议在我们不使用Authenticode时使用此参数。用法示例:

sn -vf TestLib.dll TestLib.snk

对于Authenticode验证,我们可以使用signtool或sigcheck。 示例调用可能如下所示:

signtool verify TestLib.dll
(add /pa if you are using a self-signed root certificate)
sigcheck -i TestLib.dll
(-i will show the certificate chain used to sign the assembly)

Sigcheck还可以递归地扫描目录并验证所有找到的二进制文件。我建议你看看它的帮助文档,可以找到更多有趣的运行选项(例如把二进制的哈希值提交到VirusTotal)。

简单的总结

由于很容易跳过签名验证,你可能会怀疑二进制文件签名的整个概念。考虑一下,加载时的验证不是那么重要。如果一个恶意的人获得了对二进制文件的写权限,即使对该文件进行签名也不能保护你不受TA的活动的影响。但是…签名程序集是用于证明程序集中的代码是合法的的唯一的方法,并且没有被篡改过(我鼓励你使用两个方法:强名称签名和Authenticode)。我们应该在客户端接收到二进制文件(这通常由安装程序完成)后执行第一次验证 ——以确保在传输过程中没有被篡改。接下来,在二进制文件所在的文件夹中设置有效的访问权限是非常重要的 —— 只有授权的人才能修改文件。最后,每当我们的应用程序出问题时,我们应该要求客户端在填写错误报告之前进行验证签名 ——只有这样我们才能确定该错误的确是由我们造成的。




原文发布时间为:2017年3月31日
本文作者:李白
本文来自云栖社区合作伙伴嘶吼,了解相关信息可以关注嘶吼网站。
目录
相关文章
|
4月前
|
算法 Java 测试技术
Benchmark.NET:让 C# 测试程序性能变得既酷又简单
Benchmark.NET是一款专为 .NET 平台设计的性能基准测试框架,它可以帮助你测量代码的执行时间、内存使用情况等性能指标。它就像是你代码的 "健身教练",帮助你找到瓶颈,优化性能,让你的应用跑得更快、更稳!希望这个小教程能让你在追求高性能的路上越走越远,享受编程带来的无限乐趣!
200 13
|
5月前
|
开发框架 安全 .NET
在数字化时代,.NET 技术凭借跨平台兼容性、丰富的开发工具和框架、高效的性能及强大的安全稳定性,成为软件开发的重要支柱
在数字化时代,.NET 技术凭借跨平台兼容性、丰富的开发工具和框架、高效的性能及强大的安全稳定性,成为软件开发的重要支柱。它不仅加速了应用开发进程,提升了开发质量和可靠性,还促进了创新和业务发展,培养了专业人才和技术社区,为软件开发和数字化转型做出了重要贡献。
82 5
|
6月前
|
编解码 数据安全/隐私保护
.Net PdfiumViewer 打印时无法渲染电子签名问题的解决方法
【10月更文挑战第14天】这段内容介绍了使用 PdfiumViewer 库处理 PDF 文件时遇到电子签名无法打印的问题及其解决方法。首先分析了 PdfiumViewer 默认设置或对电子签名支持不足可能导致此问题,建议更新库版本并通过 NuGet 包管理器进行升级。接着检查打印机设置和驱动程序,确保设置正确且驱动为最新版本。然后优化自定义打印代码,提高渲染分辨率,确保电子签名正确加载。最后,验证 PDF 文件格式和兼容性,必要时联系技术支持或求助技术社区。
285 2
|
6月前
|
XML 存储 安全
C#开发的程序如何良好的防止反编译被破解?ConfuserEx .NET混淆工具使用介绍
C#开发的程序如何良好的防止反编译被破解?ConfuserEx .NET混淆工具使用介绍
328 0
|
7月前
|
存储 运维
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
115 2
|
7月前
|
自然语言处理 C# 图形学
使用dnSpyEx对.NET Core程序集进行反编译、编辑和调试
使用dnSpyEx对.NET Core程序集进行反编译、编辑和调试
|
7月前
|
Ubuntu 持续交付 API
如何使用 dotnet pack 打包 .NET 跨平台程序集?
`dotnet pack` 是 .NET Core 的 NuGet 包打包工具,用于将代码打包成 NuGet 包。通过命令 `dotnet pack` 可生成 `.nupkg` 文件。使用 `--include-symbols` 和 `--include-source` 选项可分别创建包含调试符号和源文件的包。默认情况下,`dotnet pack` 会先构建项目,可通过 `--no-build` 跳过构建。此外,还可以使用 `--output` 指定输出目录、`-c` 设置配置等。示例展示了创建类库项目并打包的过程。更多详情及命令选项,请参考官方文档。
443 11
|
8月前
|
开发框架 监控 安全
.NET 应用程序安全背后究竟隐藏着多少秘密?从编码到部署全揭秘!
【8月更文挑战第28天】在数字化时代,.NET 应用程序的安全至关重要。从编码阶段到部署,需全面防护以保障系统稳定与用户数据安全。开发者应遵循安全编码规范,实施输入验证、权限管理和加密敏感信息等措施,并利用安全测试发现潜在漏洞。此外,部署时还需选择安全的服务器环境,配置 HTTPS 并实时监控应用状态,确保全方位防护。
98 3
|
8月前
|
开发框架 NoSQL .NET
使用 Asp.net core webapi 集成配置系统,提高程序的灵活和可维护性
使用 Asp.net core webapi 集成配置系统,提高程序的灵活和可维护性
120 0
|
8月前
|
Web App开发 数据采集 开发框架
在.NET程序中整合微软的Playwright,使用 Playwright 的最佳实践和技巧
在.NET程序中整合微软的Playwright,使用 Playwright 的最佳实践和技巧

热门文章

最新文章