SecureString如何“加密”?而且仍然可用

本文关键字:如何 加密 SecureString | 更新日期: 2023-09-27 18:04:53

根据MSDN SecureString内容是加密的为了额外的安全,所以如果程序被交换到磁盘,字符串内容不能被嗅闻。

我想知道这样的加密是如何可能的?该算法将是固定的,因此要么是众所周知的,要么是可扣除的(比如在行业算法中广泛使用的七种算法之一),并且在程序的某个地方必须有一个关键。因此,攻击者可以获取加密的字符串,获取密钥并解密数据。

这样的加密如何有用?

SecureString如何“加密”?而且仍然可用

我引用了一篇关于用于派生密钥的DPAPI的文章。这应该可以回答你关于SecureString的大多数问题。

是的,SecureString有缺点,不是完全安全的,有一些方法可以访问数据,例如,MSDN上提到将Hawkeye注入到进程中作为提取SecureString的一种方法。我个人还没有证实过这种说法。

DAPI密钥管理

DAPI是一种基于对称的加密技术,这意味着它使用相同的密钥来加密和解密数据。在了解如何使用DAPI的一些示例之前,有必要介绍一下DAPI如何管理其密钥。在大多数情况下,DAPI密钥管理过程是不可见的,您通常不需要担心它,这就是DAPI是一种好方法的主要原因。

在引言中我写到主密钥是由用户的登录密码生成的。这不是全部情况。实际情况是,Windows使用用户的登录密码生成一个主密钥。该主密钥使用用户的密码进行保护,然后与用户的配置文件一起存储。然后,这个主密钥被用来派生许多其他密钥,而这些其他密钥被用来保护数据。

Windows这样做的原因是它允许应用程序在生成单个密钥的过程中添加额外的信息,称为熵。如果在用户登录帐户下运行的每个应用程序都使用相同的密钥,那么每个应用程序都可以取消对DAPI保护的数据的保护。有时您可能希望应用程序能够共享受DAPI保护的数据;然而,有时你不会。通过让应用程序为密钥的生成贡献熵,那么该密钥就成为特定于应用程序的,并且被该应用程序保护的任何数据只有在知道熵的情况下才能再次受到保护。

虽然生成一个主密钥,然后使用该主密钥生成其他密钥来进行实际的加密,似乎是一种冗长的方法,但它确实有一个主要优点。由于在用户密码保护的主密钥和用于保护数据的实际密钥之间存在额外的抽象级别,这意味着当用户更改密码时,只需要重新保护主密钥;已保护的数据不需要重新保护。由于主密钥的大小比数据小得多,因此大大节省了性能。

当用户的密码改变时,当然会生成一个新的主密钥。然后使用这个新的主密钥生成新的单个密钥。然而,由于所有以前生成的单个密钥都是从旧的主密钥派生出来的,因此Windows需要存储所有以前的主密钥,它确实这样做了。Windows永远不会忘记主密钥,所有受保护的数据都标有GUID,表明使用了哪个主密钥来保护数据。因此,在适应性方面,DAPI能够应对用户密码的更改,同时确保a)受保护的数据不需要重新保护,b)以前用于保护数据的密钥仍然可用,以及c)它自动为您完成所有这些。

除非计算机是域的成员,否则DAPI只能在用于保护它的同一台计算机上保护数据。

除了允许用户级保护,因为主密钥是基于用户密码的,一个用户的受保护数据不能被另一个用户保护,DAPI还提供了机器级保护,因为主密钥是基于机器特定的信息。机器级主密钥允许应用程序存储受保护的数据,以便它可以不受应用程序的所有用户保护。上述过程的唯一区别是,主密钥是由特定于机器的信息生成的,而不是由特定于用户的信息生成的。

我看了一下它的代码,它使用Windows的advapi32来完成它的脏工作。因此,键不存储在应用程序的内存中。

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static int SystemFunction040([In, Out] SafeBSTRHandle pDataIn, [In] uint cbDataIn, [In] uint dwFlag)

RtlEncryptMemory

RtlDecryptMemory (SystemFunction041)解密。

我相信编译器也会对SecurityCriticalAttribute做一些事情。

edit这反映在4.0中。其他版本可能与不同。

正如其他人已经回答的那样,SecureString的内容使用DPAPI进行加密,因此密钥不存储在应用程序中,它们是操作系统的一部分。我不是100%肯定,但我假设SecureString使用用户特定的密钥,因此即使另一个进程获得对内存块的访问权,它也必须在相同的凭据下运行,以便使用DPAPI简单地解密内容。即使没有,机器密钥(理论上)也可以防止字符串在传输到另一个系统时被轻易解密。

SecureString更重要的是&当你使用它时。它应该用于存储需要在内存中保留"延长"一段时间的字符串数据,但不经常需要以解密形式保存这些数据。在某些时候,您将不得不将其解密为常规的旧System.StringSystem.Char[]。这是记忆中最脆弱的时候。如果您经常这样做,那么就会有多个解密字符串的副本在内存中浮动,等待收集。

作为一般规则,如果我正在读取加密数据(例如登录凭据),我需要保留不经常使用(例如PayPal或Amazon API交互),那么我将这些凭据存储/缓存为SecureString,然后根据需要解密它,只需要足够长的时间来进行web服务调用,并确保任何解密副本的生命周期只有几行代码。

使用关键块或类似于提示CLR在使用解密字符串时不应该切换上下文的方法可能也是明智的,以提高在缓存或交换内存之前收集任何解密副本的机会。

通过DPAPI的魔力:

这个类使用数据保护API (DPAPI)保护的内存模型来存储它的数据。换句话说,当数据存储在SecureString中时,它总是以加密的形式存在。加密密钥由本地安全授权子系统(LSASS.EXE)管理,通过DPAPI,可以通过进程间通信对数据进行解密。