前言
我深入研究了 .Net 的 x509certificate2 和 x509certificate 对象,发现这两个证书类默认会将私钥序列化到磁盘上,原因是在 x509certificate2 和 x509certificate 类的构造函数中使用了 Persist-Key 选项,这些选项使用了默认的 CspProviderFlags 选项集,其中不包括 CspProviderFlags.CreateEphemeralKey。这可以通过许多显示 .Net 源代码的网站以及 Resharper 的副本来验证。
进一步查看代码,实际上这些 .Net 对象似乎只是一个中央旧式 crypto32.dll 加密上下文的包装器。令我担忧的是,在检索器和设置器 PublicKey 字段的 getters 和 setters 中,它似乎明确地在磁盘文件存储中查找公钥和私钥,尽管我原本希望它们只存在于内存中。基于这个知识,即使我不想这样做,它似乎也会将我的私钥存储在此存储中,因为构造函数不允许您更改此选项并根据我查看文档时看到的构造函数列表关闭它;因此,如果您从 PrivateKey 字段 get() 或 set(),它实际上只是从幕后的文件存储中收集它。
我提出问题的原因
我认为被迫泄露我的私钥到文件系统 - 即使是暂时的 - 是一个巨大的安全问题,所以我不想这样做。我也不信任证书存储。即使您不同意我的观点,我个人认为即使我不打算这样做,被迫将我的私钥序列化到磁盘上也是一个巨大的安全问题,所以我不想这样做。
目标
我的当前目标只是能够在 C# 中生成 RSA 公钥和私钥 X509 密钥,而无需在创建过程中强制将它们写入硬盘,我发现很少有有效的选择,因为我找到的所有选择都似乎存在隐藏缺陷或公开已知问题。
目标不是永远不将它们写入硬盘,而是除非使用我编写并知道安全的代码,否则不会这样做,以确保它们在休息时完全加密;即,除非在完全加密密钥之后明确要求,否则不应该这样做。
目标 tldr;
简而言之:我想使用C#在Windows上编写安全软件,以生成不强制我将私钥导出、导入或泄露到文件系统中的509证书。我希望尽可能少地使用第三方代码来在Windows上创建自己的X509Certificates。我考虑过的选项
- 经过深入研究,发现标准的.Net类型由于上述问题而缺乏足够的内部CSP设置访问权限,因为构造函数和访问方式没有暴露它们,所使用的默认值也被认为是不安全的。
- Bouncy Castle库一开始看起来是个不错的选择,但是当它将它们转换为标准的.Net类型时,它会像普通的.Net类型一样积极地将我的公钥和私钥暴露给文件系统,因为这些对象在幕后存储东西,没有询问BC是否可以进行。
- 我试图找到一个crypto32.dll P-invoke选项,它允许我不序列化我的密钥,只发现所有我能找到的都确实如此,即使我不想要它们。而且很难找到关于它的数据,所以我主要只看了源代码,只发现它似乎不喜欢我试图不存储密钥。
- 尽管花费了几天时间研究这个主题,但我无法找到任何其他选项。即使Stack Overflow在多数情况下建议使用BC或本地类型,但这并不能解决我的问题。
问题
如何在以下情况下最好地创建自己的X509证书,而无需执行以下任何操作,因为我认为它们都是安全问题:
- 使用makecert.exe需要我在服务器上允许我的进程执行权限,这可能会导致安全问题。更不用说,我无法审计代码,而且我敢打赌即使我不想让它们被存储,它仍然会将密钥存储在证书存储中。
- C#/.Net x509certificate2和x509certificate对象的使用默认通过其对证书存储的调用将生成的公钥序列化到磁盘上,因此这不是一个选项;目标是以安全的方式进行操作,所以我生成的所有私钥都被存储在磁盘上,任何人都可以访问它们,这让我感觉不好。而且很难找到证书存储的数据;因此我无法验证它的安全性,必须考虑它不是一个有效的选项。
- 使用第三方库(如Bouncy Castle)会创建更新场景和必须信任的第三方,从而创建更大的攻击面;可悲的是,它们都使用相同的转换,受到我个人称之为“隐藏私钥存储”问题的影响,它们试图使用具有泄漏密钥问题的相同对象与标准System.Security.Cryptography API保持一致。因此,如果我们总体上希望尽可能保持系统安全......那种泄漏不是一个选项。