所有不同的X509KeyStorageFlags的理由是什么?

31

今天,我的一个同事遇到了与这些有关的另一个错误!我自己在过去也发现这些标志非常令人沮丧,因为如果在实例化X509Certificate2对象、导出它们或将它们保存在X509Store中时,稍微出错,就可能会陷入各种奇怪的错误情况,例如:

  • 即使你在机器存储中拥有该证书,也无法意外地告诉NETSH.exe或ASP.net使用某个SSL证书[按其指纹]。
  • 意外地,您可以导出证书数据,但它会在使用.Export()时导出不带私钥的证书。
  • 意外地,您的单元测试开始在较新的Windows版本上失败,显然是因为您没有使用正确的标志。

是的,它们都有文档(其中一些文档几乎似乎是有意义的),但为什么必须这么复杂呢?


1
安全性,安全性,安全性。不同的证书有不同的用途,所有标志都旨在帮助各种用途。您恰好只使用IIS/ASP.NET的服务器证书,因此您会发现一些标志/行为“意外”。然而,其他人可能会轻松地发现它们有用和关键。因此,没有合适的答案。 - Lex Li
@Lex Li 嗯,我想它应该有助于提高安全性...只是我不太明白它是如何做到的。这可能就是为什么我经常猜错标志的原因。哎呀,这是否意味着我使用了错误的标志而写入了安全漏洞? - Tim Lovell-Smith
很不幸,如果您使用错误的标志,可能会存在安全问题。例如,某些证书必须安装为不可导出的私钥(与您的情况不同),如果您将它们安装为可导出的私钥,则可能会在您眼皮底下发生损坏。确切场景不易迭代,但如果您认识一些具有安全背景的人,可以咨询并学习更多。这对于SO来说范围太广了。 - Lex Li
1个回答

92

主要原因是今天的复杂程度源于昨天的复杂程度,没有人提出更简单的解决方案。

我无法提供线性叙述,所以请耐心忍受必要的来回交织。

PFX/PKCS#12文件是什么?

虽然我无法完全说出PFX的起源,但Windows读取和写入它们的函数名称中有一个提示:PFXImportCertStorePFXExportCertStore。它们包含许多不同的实体(证书、私钥和其他内容),可以使用属性标识符进行关联。它们似乎被设计为整个证书存储的导入/导出机制,例如所有CurrentUser\My。但由于一种存储类型是"内存存储"(任意集合),.NET的导入/导出是有意义的,但也存在一些复杂性(来自.NET之前)。

Windows私钥

Windows支持许多不同的私钥存储位置,但对于旧的加密API,它们归结为4部分寻址方案:

  • 加密提供程序的名称
  • 密钥容器的名称
  • 标识符,指示这是机器相关密钥还是用户相关密钥
  • 标识符,指示这是"签名"密钥还是"交换"密钥。

在CNG中,这被简化为一个三部分的方案:

  • 存储引擎的名称
  • 密钥的名称
  • 标识符,指示这是一个机器相关密钥还是用户相关密钥。

为什么需要机器或非机器标识符?

CAPI和CNG都支持直接与命名密钥交互。因此,您可以创建一个名为“EmailDecryption”的密钥。系统上的另一个用户创建了相同名称的密钥。那么这能行吗?很可能可以。因此,它确实起作用!这是因为不同的密钥保存在与创建它们的用户相关联的上下文中。

但现在您想要一个可以由多个用户使用的密钥。这不是通常需要的东西,因此不是默认选项。这是一种选择。于是就有了CRYPT_MACHINE_KEYSET标志。

我在这里要说的是,我听说现在不鼓励直接使用命名密钥; CAPI/CNG团队更喜欢GUID命名密钥,并建议您通过证书存储与其进行交互。但这是演变的一部分。

导入PFX做了什么?

PFXImportCertStore将PFX中的所有证书复制到提供的存储中。它还会导入(CryptImportKeyBCryptImportKey,具体取决于它认为需要什么)。然后,对于它导入的每个密钥,它通过PFX中的属性值找到匹配的证书,并在证书存储表示中设置一个属性,表示“这是我的4部分标识符”(CNG密钥只将第4部分设置为0);这实际上是证书了解其私钥的全部内容。(PFX是一种非常复杂的文件格式,前提是没有使用“奇怪的部分”,此描述才是正确的)
密钥生命周期
Windows私钥永久存在,或者直到有人删除它们。
因此,当PFX导入它们时,它们将永远存在。如果您导入到CurrentUser\My,则这是有意义的。如果您正在进行某些短暂的操作,则不太合适。
.NET反转了关系/使其“太容易”
Windows设计(大多数情况下)是您与证书存储交互,并从证书存储中获取证书。.NET稍后出现,(根据看到应用程序真正在做什么所推断的)将证书作为顶级对象,并将存储视为次要对象。
因为Windows证书(实际上是“存储证书元素”)“知道”它们的私钥,.NET证书也“知道”它们的私钥。

哦,但MMC证书管理器说它可以导出带有私钥的证书(成为PFX),为什么证书构造器不能接受这些字节以及“只是一个证书”的格式?好吧,现在它可以了。

协调生命周期

你打开一些字节作为X509Certificate/X509Certificate2。它是一个带有“无密码”的PFX(通过任何可能为真的各种方式)。你发现它是错误的,然后让证书离开垃圾收集器。那个私钥将永远存在,所以你的硬盘慢慢填满,密钥存储访问变得越来越慢。然后你生气了,重新格式化你的电脑。

那似乎很糟糕,所以.NET做的是当(证书的字段)正在被垃圾收集(实际上是最终处理)时,它告诉CAPI(或CNG)删除该密钥。现在事情按预期工作了,对吗?好吧,只要程序不异常终止。

哦,你把它添加到了持久存储中?但是我将在新的证书存储实体“知道”如何找到私钥之后删除私钥。那看起来很糟糕。

输入X509KeyStorageFlags.PersistKeySet

PersistKeySet表示“不要做那个删除的事情”。这是为当你打算将证书添加到X509Store时使用的。

如果你想要相同的行为而不指定标志,请调用Environment.FailFast,或在导入后拔掉电脑。

关于机器或用户位的说明

在.NET中,你可以轻松地将证书收集到一个集合中,并在其上调用Export。如果其中一些具有机器密钥,而另一些具有用户密钥怎么办?这时可以使用PFXExportCertStore。当导出机器密钥时,它会写下一个标识符,表示它是一个机器密钥,因此导入会将其放回到相同的位置。

通常情况下是这样的。也许你从一台机器上导出了一个机器密钥,并且想在另一台非管理员的机器上仅作为检查。好吧,你可以指定CRYPT_USER_KEYSET,又称X509KeyStorageFlags.UserKeySet

或者你在一台计算机上以用户身份创建了它,但想将其作为机器密钥在另一台计算机上使用?没问题。 CRYPT_MACHINE_KEYSET / X509KeyStorageFlags.MachineKeySet

我真的需要“临时”文件吗?

如果你只是检查PFX文件,或者以其他方式想要在临时基础上处理它们,为什么还要将密钥写入磁盘呢?好吧,Windows Vista说,我们可以直接将私钥加载到加密密钥对象中,并告诉你指针。

PKCS12_NO_PERSIST_KEY / X509KeyStorageFlags.EphemeralKeySet

我想,如果Windows在NT4中具有此功能,那么这将成为.NET的默认设置。现在它不能是默认设置,因为太多的东西依赖于“正常”导入的内部工作方式来检测私钥是否可用。

最后两个是什么情况?

PFXImportCertStore的默认模式是私钥不可再导出。如果想要更改这一点,可以指定CRYPT_EXPORTABLE / X509KeyStorageFlags.Exportable

CAPI和CNG都支持一种机制,即软件密钥在使用私钥之前需要经过同意或输入密码(类似于智能卡的PIN提示),但您必须在首次创建(或导入)密钥时声明。因此,PFXImportCertStore允许您指定CRYPT_USER_PROTECTED(.NET将其公开为X509KeyStorageFlags.UserProtected)。

这最后两个只对“一个私钥”PFX有意义,因为它们适用于所有密钥。它们也不能涵盖原始密钥可能具有的全部选项…… CNG和CAPI都支持“可存档”密钥,这意味着“可导出一次”。机器密钥上的自定义ACL在PFX中也没有得到支持。

总结

对于证书(或证书集合),一切都很简单。一旦涉及私钥,事情就变得混乱了,Windows证书(存储)上的抽象变得有点薄弱,您需要了解持久性模型和存储模型。


8
哇,那是一个非常丰富和详细的答案。我希望我能给它超过+1的赞。非常感谢您与大家分享这个! - Tim Lovell-Smith
2
哦,是的...赏金! - Tim Lovell-Smith
你知道安装pfx文件时,使用X509Certificate2.Install()和X509KeyStorageFlags.PersistKeySet以及X509KeyStorageFlags.MachineKeySet标志,在某些机器上会导致证书私钥在之后被删除的原因吗?这个“之后”可能是在2个月或1天之后(可能是在重启后,不确定)。 - axk

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