【问题标题】:Do I need to publish the public key from .snk file?我需要从 .snk 文件中发布公钥吗?
【发布时间】:2010-10-22 08:49:35
【问题描述】:

从 sn.exe 实用程序和 this question 的描述中,我看到公钥的副本已添加到使用强名称签名的每个程序集中。这足以验证程序集二进制文件没有被更改。

但是如何验证给定的程序集是否真的用给定的密钥对签名并由给定的公司编译?任何人都可以生成自己的密钥对,生成一些程序集并用他的密钥对进行签名。我是否需要发布公钥,以便那些想要验证程序集来源的人可以比较公钥?如果是这样,最好的方法是什么?

【问题讨论】:

    标签: .net assemblies strongname


    【解决方案1】:

    不,您不需要在程序集之外发布您的公钥,因为它已被散列并作为令牌存储在客户端应用程序内的引用旁边:

    AssemblyName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bcd6707151635d07"
    

    这提供了一种方法来确保程序集的所有未来版本都针对相同的密钥对进行编译,因此来自相同的发布者。

    关于如何拥有此信息的更多详细信息可以在我的其他答案中找到。

    【讨论】:

      【解决方案2】:

      我一直理解签署程序集的目的是进行运行时检查,以确保对代码的升级来自受信任的来源。实际上,他们正试图避免这种情况:

      我从 A 公司购买了一个加密库,不久之后,B 公司获得了我的电子邮件详细信息,并向我发送了一个免费升级到该程序集,假装是 A 公司的一个重大安全漏洞修复程序,实际上它确实注入了一个隐藏的方法我的代码将我试图加密的所有数据发送到 B 公司的服务器。假设我是个白痴,并且盲目地将这个新的 dll 添加到我的应用程序的 bin 目录中,那么它没有使用与旧版本相同的私钥签名这一事实将在运行时被拾取,从而导致异常并保护我的数据。

      因此,我认为您没有理由在程序集之外发布公钥,因为它不应该保证原始文件来自特定供应商,只是所有后续版本都来自与首先。

      Wikipedia 说了很多相同的话(只是用词少得多)。


      已编辑以添加更多信息以使这一点更清晰...

      我认为首先需要澄清的是公钥和私钥对是唯一的。这意味着知道公钥并不足以重建程序集以使其通过相同的哈希检查。

      所以用户 Z 正在使用来自公司 A 的加密库,该库位于他的应用程序 bin 文件夹中。为此,他按如下方式引用 DLL:

      Encryption, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bcd6707151635d07"
      

      公钥的目的是为我们提供three benefits,我将一次处理一个。

      首先,它为程序集提供了一个唯一的名称——这没有给我们额外的安全性,但确实阻止了 C 风格的 dll 地狱,两个不同的公司可以发布两个不同的库,称为 Encyption 版本 1.0.0.0,而你将无法将它们存储在同一目录中,因为无法区分它们。

      其次,它阻止了我在原始帖子中概述的情况。 B 公司不可能创建另一个版本的加密库,它也是 1.0.0.0 版本并具有相同的公钥(这样整个名称就会匹配,并且您会调用他们的代码而不是 A 公司的代码)。他们不能这样做,因为如果他们的公钥匹配,那么私钥也必须匹配(因为每个 pair 都是唯一的)。他们可以让私钥匹配的唯一方法是破坏 A 公司的安全性。

      最后,它确保了文件的完整性(通过损坏或恶意代码注入进行更改)。 dll 在运行时(当它是私有的并且在应用程序的 bin 文件夹中时)或安装时(当您将它放在 GAC 中时)使用公钥进行哈希处理,并将该哈希值与私有加密的哈希值进行比较密钥并存储在程序集中。 This page 有一些图表和更多细节。再一次,伪造此哈希的唯一方法是知道私钥。

      因此,为了涵盖我上面描述的特定场景,假设我是 B 公司,试图向用户 Z 提供加密 dll 的恶意版本。我的第一次尝试是简单地使用名称加密和版本 1.0.0.0 制作我自己的 dll,并使用我自己的密钥对对其进行签名。不幸的是,我无法更改用户 Z 应用程序中的参考代码,因此它无法通过全名检查并且不会被加载。 “好吧,”我一边捻着小胡子一边说,“我将简单地更改我的程序集的公钥以匹配 A 公司的公钥。”完成此操作后,名称检查通过,但哈希检查将失败,因为用户 Z 的应用程序将无法使用公钥(公司A's) 已提供。因此,B 公司创建伪装成 A 公司的库的唯一方法是知道 A 公司的私钥。除了在程序集的原始版本中,这些安全检查都不依赖于 A 公司在其他任何地方发布公钥。

      另请注意,所有这些功能都不能确保(也不声称确保)原始程序集来自 A 公司,由其他系统处理,例如 Verisign 或 Microsoft 的 Authenticode 服务,他们只确保一旦您引用了程序集,只有 A 公司可以更改该代码。

      【讨论】:

      • 这完全不清楚,也没有在链接的文章中明确提及。为了完成所有这些,运行时需要缓存每个程序集的哈希值,并在加载程序集时验证这一点。它真的这样做吗?
      • 根据 Jeffery Richter 的“CLR via C#”,当程序集不在 GAC 中时,它会执行此操作。从书中引用:“当从 GAC 以外的位置加载强命名程序集时,CLR 会在加载程序集时比较哈希值。换句话说,每次应用程序执行并加载程序集时都会执行文件的哈希值. 这种性能损失是为了确定程序集文件的内容没有被篡改而做出的权衡。”
      • 我还将添加来自“C# via the CLR”(顺便说一下强烈推荐的书)的这句话:“重要:此机制仅确保文件的内容没有被篡改。除非您绝对肯定发布者生成了您拥有的公钥,并且您确定发布者的私钥从未被泄露,否则该机制不允许您知道发布者是谁。”因此,即使他们确实将公钥与程序集分开发布,它仍然不能真正让您验证发布者。
      • 那句话是关于验证散列的——来自程序集的散列与加载时为该程序集计算的散列进行比较。它没有说明在其他任何地方存储先前计算的哈希值。所以我不明白这将如何防止公司 B 更换磁盘上的组件。新放置的程序集将使用另一个密钥进行签名,但仍然具有该另一个密钥的有效哈希。
      • 这仅仅是因为对库的引用在调用代码中包含了“PublicKeyToken”吗?
      【解决方案3】:
      1. 您可以去一些 3d 方证书提供商(例如 VeriSign)并从他们那里购买证书 (Code Signing At Verisign)。
      2. 您使用带有您的公司名称、URL 等的给定证书来签署您的代码。
      3. 我下载了您的应用,然后查看您的应用所使用的证书列表。
      4. 我使用您的证书,返回 VeriSign 并验证该证书确实已颁发给 MyCompany, LLC。
      5. 我查看了已用于颁发您的证书的证书,并确认 VeriSign 是其中之一(Windows 附带安装了一些受信任的证书)。

      总结:
      您不仅要验证代码没有被修改过,而且还使用由您信任的一方颁发的证书进行签名。

      【讨论】:

      • 我明白这一切,这就是我们实际所做的。但这与强名称和 .snk 文件无关,不是吗?
      • 我已经有一段时间没有这样做了。但是 IIRK 您使用证书的公钥来签署您的代码。 IE。 .snk 是从您使用 makecert.exe 从 VeriSign 获得的 .cer 文件中生成/导出的。这样签名代码实际上做了两件事 1) 防止代码被修改(保护) 2) 验证发布者。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-29
      • 1970-01-01
      • 2011-01-10
      • 1970-01-01
      相关资源
      最近更新 更多