公钥令牌的作用是什么?

83
公钥标记的作用是什么?它在解密签名哈希中扮演了什么角色?在全局程序集缓存(GAC)中,为什么有这么多具有相同公钥标记的微软程序集?

1
@Bombe,解密签名哈希值是正确的,只有一个错误。 - H H
请参阅https://dev59.com/U3RB5IYBdhLWcg3wn4UL - Colonel Panic
如果您熟悉SSH或PGP,那么指纹就是您所知道的公钥的缩略版本,更容易通过肉眼检查。 - Colonel Panic
5个回答

170

公钥令牌的作用是什么?

公钥令牌是一个小数字,它是代表公钥的便捷“令牌”。公钥非常长;公钥令牌的目的是让您可以引用密钥而不必说出整个密钥。有点像说“指环王”这五个单词代表了一个50万字的小说。如果每次想要谈论它,都必须说出那50万个单词,那将会相当不方便。

公钥令牌在解密签名哈希中起到了什么作用吗?

没有。公钥令牌中没有任何“信息”。它只是一个数字,代表一个公钥。它本身不是公钥。

为什么微软的许多程序集具有相同的公钥令牌?

因为它们都使用了相同的私钥——微软的私钥——因此都是用相同的公钥进行验证,因此都具有相同的公钥令牌。


4
艾瑞克,你回答得真是精炼且完美! - Aditya Bokade
通过代码检查Assembly.FullName属性,可以使用命令行工具获取它,请参见此答案 - Scott Chamberlain

15

来自维基百科

"公钥标记用于使程序集名称独特。因此,两个强命名的程序集可以具有相同的PE文件名称,但.NET将识别它们为不同的程序集。Windows文件系统(FAT32和NTFS)仅识别PE文件名称,因此具有相同PE文件名称(但不同文化、版本或公钥标记)的两个程序集不能存在于同一Windows文件夹中。为解决这个问题,.NET引入了一个称为GAC(全局程序集缓存)的东西,它被.NET CLR视为单个文件夹,但实际上是使用嵌套的NTFS(或FAT32)文件夹实现的。

为了防止欺骗攻击,其中黑客会尝试将装配体作为其他内容传递,该装配体使用私钥签名。拟定装配体的开发人员保持私钥机密,因此黑客无法访问它也无法猜测它。因此,黑客无法将其装配体伪装成其他物品,也无法在更改后正确签署它。签署程序集涉及对程序集的重要部分进行哈希,并使用私钥加密哈希。已签名的哈希与公钥一起存储在程序集中。 公钥将解密已签名的哈希。当CLR加载强命名的程序集时,它将从程序集生成哈希,然后将其与解密的哈希进行比较。如果比较成功,则意味着文件中的公钥(因此也是公钥标记)与用于签署程序集的私钥相关联。这意味着程序集中的公钥是程序集发布者的公钥,因此可以防止欺骗攻击。


6

哈希值有点像“指纹”。它使用签名者拥有(并且仅知道)的私钥进行签名。如果您知道签名者的公钥,您可以检查哈希值是否真的来自签名者,因此数据/文件是否真的来自签名者(并且未更改)。在GAC中对于某些文件相同的公钥意味着“所有文件都由同一签名者签名”。


所以令牌只是使用的公钥的指示器,对加密/解密没有直接参与。 - softwarematter

5
公钥令牌是真实公钥的一部分可读摘录。完整的公钥存储在已签名程序集中,并用于解密签名(=加密哈希)。加载器使用此功能来验证内容未被篡改或损坏。原始哈希由作者使用私钥加密,只有持有该密钥的人才能生成有效的签名。
每个公司(或部门)应只使用1对密钥,这就是为什么您在GAC中看到了相同PKT组的原因。

1

我想在之前的回答中补充一些内容(特别是引用自维基百科的那个回答),即通过公钥/私钥进行强命名并不能保护您免受被更改的程序集或防止有人篡改您的程序集。

首先,强名称并不能保证程序集是可信的。您只有公钥/公钥令牌,但不知道签名者是谁(除非他们以某种方式宣布拥有程序集的公钥)。

例如,黑客可以获取您的程序集,从中删除强名称(有工具可以实现此操作),再用自己的强名称对其进行签名。 对于信任,有一种不同类型的数字代码签名与证书有关。它涉及第三方检查您和您的公司,并且不是免费的。请查看 Authenticode 技术:

https://msdn.microsoft.com/en-us/library/ms537359(v=vs.85).aspx

其次,接下来的讨论中简要描述了一种暴力攻击方法,用于获取具有相同公钥令牌的公钥/私钥对,该方法会为篡改的程序集生成相同的哈希值。

https://groups.google.com/forum/?hl=en#!topic/microsoft.public.dotnet.security/Jo6PqypxJN8

我必须指出,这个问题可以通过增强的强名称https://learn.microsoft.com/en-us/dotnet/framework/app-domains/enhanced-strong-naming来解决。
讨论中还提到了一个漏洞,允许跳过程序集验证并在运行时加载篡改的程序集。详细的研究在这里,该漏洞已在较新版本的.Net框架中修复(因此旧的.Net 1存在该漏洞)。

http://www.grimes.nildram.co.uk/workshops/fusionWSCrackThree.htm

第三,启动.Net 3.5 sp1以提高装配件的负载性能,默认情况下不验证完全信任的装配件。

https://learn.microsoft.com/en-us/dotnet/framework/app-domains/how-to-disable-the-strong-name-bypass-feature

装配的条件: https://blogs.msdn.microsoft.com/shawnfa/2008/05/14/strong-name-bypass/ 关于此问题在Stack Overflow上的讨论: Are signed .net assemblies ever fully verified when loaded, to check they haven't been modified? 据我了解,这意味着装配在加载时未进行哈希处理以检查其是否已被篡改。
最后,我想提到强名称的优缺点存在争议,因为它们要求您指定装配版本。Microsoft从一些产品中删除了强名称: https://www.pedrolamas.com/2016/03/01/still-strong-naming-your-assemblies-you-do-know-its-2016-right/ 总结:我想总结一下所提到的所有要点。当我遇到强名称时,我被 MSDN 和维基百科误导,认为它可以为程序集提供某种防御。我想:“太棒了”,并且强命名一直留存在我的记忆中作为一种保护机制。直到我开始考虑我的带有私钥的 snk 文件的安全性,并且我的同事告诉我那不是“太棒了”。于是我做了一些研究。 我学到的第一件事是,强名称并不意味着信任,应该使用证书进行验证。 尽管如此,我认为如果我保持我的私钥安全,那么篡改的程序集将不会由我签名,这意味着如果有人修改我的程序集,那么他也必须修改签名,否则修改后的程序集将无法被 CLR 加载。现在我不认为强名称可以保证这一点。因此,您只应依赖它来确保程序集的唯一性。

附言:对于带有许多参考资料的长篇文章,我感到非常抱歉。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接