在.NET C # 中存储加密密钥的最佳方法

在我们的应用程序中,我们有许多敏感的配置设置,我们将它们存储在一个 xml 文件中,该文件再次加密。

必须在运行时解密此安全文件,并读取配置值。但问题是,密钥和初始向量在代码中是硬编码的,因此任何人都可以通过反射器读取它。

在.NET 中存储加密密钥的最佳方法是什么,以便没有人能够使用反射器读取它们?

69886 次浏览

如果你不能在反射器中读取它们,你怎么期望程序能读取它们?你可以通过把它们分解并存储在各个地方来混淆它们,但是(AFAIK)一旦你要求你的程序能够读取它们,那么任何能够访问你的代码的人也可以读取它们。

不要忘记也可以访问内存中的值(咳嗽 SecureString)。

如果希望保护数据不受其他用户的影响,请查看 ProtectedData类。

(免责声明: 保护您的数据以创建复制保护方案不包括在本答案中)。

此类使用 Windows 中的 DPAPI 在用户或计算机级别对数据进行加密和解密。

使用 ProtectedData/DPAPI 使您无需自己处理密钥和保护数据。您可以选择为当前用户保护数据。数据可以从不同的计算机读取,由相同的域用户。

如果您想创建自己的密钥。您可以为每个用户/机器创建一个密钥,并将此密钥存储在注册表中。因为注册表可以是安全的,所以只有当前用户可以将密钥读回。我知道注册表有不好的报应,但实际上它非常善于存储这样的数据。

附注: 不要把 IV 放在你的代码中。每次都创建一个新的 IV,并把它放在数据前面。

通常,应该为每个会话创建一个新的密钥和 IV,并且不应该存储密钥和 IV 以供以后的会话使用。

要将对称密钥和 IV 传递给远程方,通常需要使用非对称加密来加密对称密钥和 IV。通过不安全的网络发送这些值而不加密它们是非常不安全的,因为任何拦截这些值的人都可以解密您的数据。有关这个加密和传输密钥和 IV 过程的更多信息,请参见 创建加密方案

你应该使用机器密钥存储,它是一个安全的存储,特别是为了这个目的。例如:

CspParameters cspParams = new CspParameters(PROV_RSA_FULL, null, KEYNAME);


cspParams.Flags = CspProviderFlags.UseMachineKeyStore;


RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cspParams);

其中 KEYNAME 是一个自定义字符串,可用于稍后检索密钥。

有关更多示例,请参见这个问题: 如何在机器级 RSA 密钥容器中存储公钥

如何将文件加密/解密密钥存储在远程服务器上,并通过 Web 服务将其通过 https 传输到应用程序?这样密钥就会保留在计算机的内存中,而不会进入源代码文件。

不过,这需要运行应用程序的任何人与密钥服务器建立连接。

安装应用程序时,创建一组新的 RSA 密钥,然后使用私钥作为密码,用 AES 对数据进行加密。由于 Windows 在创建 RSA 私钥的 PC 上安全地存储 RSA 私钥,因此只有 被创造出来数据的计算机才能对数据进行解密,因为只有该计算机才有必要的密钥。

值得一提的是,对于将数据存储在数据库中的应用程序来说,每台机器或每个用户的任何解决方案都不是特别有用,因为在应用程序运行时,数据库可能会被多个用户在多台机器上读取。大多数用例不能假设给定用户发布的数据只能被 读取。

这是在应用程序中存储和生成/使用加密密钥的 天啊最佳实践。如果您的使用要求不能适应这个方案,您的里程可能会有所不同。

首先,与我的应用程序一起,我将应用程序特定的配置器分发给我的客户。此配置由我的客户的管理员运行,他安装应用程序和驻留在 Microsoft SQL Server 上的数据库。

在运行安装程序之后,管理员运行我的配置程序。配置器允许他指定敏感信息,例如应用程序发送电子邮件所需的 SMTP 服务器凭据等。

配置器使用私有主密钥,该主密钥硬编码在 DLL 中。每个分发版本的嵌入密钥不同。配置程序对敏感信息进行加密,然后将其存储在注册表中。主密钥还用于加密/解密在运行时生成的其他加密密钥,以加密/解密应用程序数据。

在应用程序运行时,会为一般应用程序数据加密生成另一个密钥——密码和其他敏感信息以加密形式存储在数据库中。使用前面提到的 DLL 中的主密钥对该密钥进行加密,然后将其存储在注册表中。

每次应用程序需要在数据库之间加密或解密数据时,它都使用主密钥来解密应用程序密钥。应用程序键每次都与不同的 IV 组合在一起。每个新的 IV 只是简单地预先对数据,正如这里的其他一些答案所描述的那样。

解密时,应用程序只需将 IV 从数据有效负载的前端剪下,并使用它和注册表中的应用程序密钥一起解密有效负载的其余部分。

通过使用这个方案,唯一的风险是可以从内存中读取它。如果您非常关心这个问题,可以使用安全字符串来最小化这种风险。我的大多数客户并不希望他们的用户连接到主板的 JTAG 端口,并尝试读取和破译内存内容,但是,嘿... 你的英里数可能会有所不同。