开发者社区> 玄学酱> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

浅谈.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日
本文作者:李白
本文来自云栖社区合作伙伴嘶吼,了解相关信息可以关注嘶吼网站。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【刷穿 LeetCode】剑指 Offer 22. 链表中倒数第k个节点 :「栈/队列」&「差值法」&「快慢指针」
【刷穿 LeetCode】剑指 Offer 22. 链表中倒数第k个节点 :「栈/队列」&「差值法」&「快慢指针」
21 0
Suro —— Netflix开源的分布式数据管道系统
Netflix近日开源了一个叫做Suro的工具。Suro从多个应用服务器收集事件数据,以便发送其他数据平台(例如Hadoop和Elasticsearch)。随着Suro的开源,Netfix的这项大数据上的创新有望成为主流技术。
78 0
Flutter中ListView加载图片数据的优化
在使用ListView懒加载模式时,当ListView的Item中有图片信息时,在快速滚动过程中会大量的浪费流量与内存,甚至会造成在滚动过程中页面的卡顿效果。 在这里提出优化方案,当开始滚动时不加载图片,滚动结束后再加载图片,这个优化方案实现的效果如下图所示,在快速滑动列表数据时,图片未加载,运行内存无明显波动。
506 0
ViewPager详解(二)——自动轮播和手动切换完整示例
MainActivity如下: package cn.ww; import android.app.Activity; import android.
1076 0
viewpager循环滚动和自动轮播的问题
ViewPager是一个常用的android组件,不过通常我们使用ViewPager的时候不能实现左右无限循环滑动,在滑到边界的时候会看到一个不能翻页的动画,可能影响用户体验。此外,某些区域性的ViewPager(例如展示广告或者公告之类的ViewPager),可能需要自动轮播的效果,即用户在不用滑动的情况下就能够看到其他页面的信息。 为此我查阅了网络上现有的一些关于实现这样效果
1258 0
[转载]一名优秀的Flex开发者需要知道的10样东西
原文:http://blog.flexdevelopers.com/2010/04/10-things-good-flex-developer-should.html 翻译:http://bbs.9ria.com/viewthread.php?tid=54144&from=recommend_f   要想成为一名优秀的Flex编程人员,单单知道怎么样去使用Flex内建的容器和组件是不够的,而且是远远不够。
962 0
+关注
玄学酱
这个时候,玄酱是不是应该说点什么...
20683
文章
438
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载