今天,一位同事又发现了另一个与此有关的错误!我发现这些标志确实让我感到沮丧,因为如果在实例化X509Certificate2对象,导出它们或将它们保存在X509Store中时,将它们弄得有些错误,则可能会遇到各种奇怪的错误,例如:>
是的,它们都已被全部记录(某些文件似乎很有意义),但是为什么它必须如此复杂?
答案 0 :(得分:16)
主要是,今天必须如此复杂,因为昨天是如此复杂,而且没人能提出更简单的方法。
在这里我无法提出线性叙述,因此请耐心要求进行来回编织。
虽然我不能完全说出PFX的起源,但Windows函数的名称中有一个线索可以读写:PFXImportCertStore和PFXExportCertStore。它们包含许多单独的实体(证书,私钥和其他东西),可以使用属性标识符进行相互关联。它们似乎被设计为整个证书存储区(如所有CurrentUser \ My)的导出/导入机制。但是,由于一种存储是“内存存储”(任意集合),因此.NET导入/导出很有意义,但是会带来一些麻烦(来自.NET之前)。
Windows在私钥上支持许多不同的位置,但是对于传统的加密API,它们归结为4部分寻址方案:
这已简化为CNG的三部分方案:
CAPI和CNG都支持与命名键直接交互。因此,您创建了一个名为“ EmailDecryption”的密钥。系统上的另一个用户创建了相同名称的密钥。应该行吗?我们可能会。所以,真主党,确实如此!单独的密钥,因为它们被保存在与创建它们的用户相关的上下文中。
但是现在您想要一个可以供多个用户使用的密钥。这不是您通常想要的东西,因此不是默认值。这是一个选择。 CRYPT_MACHINE_KEYSET
标志诞生了。
我将继续在这里说,我听说现在不鼓励直接使用命名键; CAPI / CNG团队更喜欢使用GUID命名的密钥,并且您可以通过证书存储与它们进行交互。但这是进化的一部分。
PFXImportCertStore将所有证书从PFX复制到提供的存储中。它还会导入(CryptImportKey或BCryptImportKey,具体取决于它的需求)。然后,对于它导入的每个密钥,它都会找到匹配的证书(通过PFX中的属性值),并在证书存储表示中为“这是我的4部分标识符”设置一个属性(CNG密钥仅设置了第4个部分为0);这实际上是证书所知道的有关其私钥的全部信息。
(PFX是一种非常复杂的文件格式,如果没有“怪异的部分”得到利用,则此描述是正确的)
Windows私钥将永久存在,或者直到有人将其删除为止。
因此,当PFX导入它们时,它们将永远存在。如果要导入到CurrentUser \ My,则这很有意义。如果您只是暂时做某事,那就没意义了。
(大多数情况下)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证书(存储)的抽象也会变得稀疏,您需要了解持久性模型和存储模型。