我在想,这样的加密是如何实现的呢?算法必须是固定的,因此可能是众所周知或可推导的(比如在行业中广泛使用的七种算法之一),而且程序中必须有一个密钥。因此,攻击者可以获取加密字符串、密钥,然后解密数据。
那么,这样的加密有什么用处呢?
我引用了一篇关于 DPAPI 的文章,它用于推导密钥。这应该能回答你对 SecureString 的大部分问题。
是的,SecureString 有缺点,不完全安全,有方法可以访问数据,例如,在 MSDN 上提到注入 Hawkeye 到进程中是一种提取 SecureString 的方法。我个人没有验证这个说法。
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
做一些处理。
编辑 这是使用4.0反映的。其他版本可能会有所不同。
通过 DPAPI 魔法:
这个类使用数据保护 API(DPAPI)受保护的内存模型来存储其数据。换句话说,在 SecureString 中存储数据时,数据始终处于加密状态。 加密密钥由本地安全机构子系统(LSASS.EXE)管理,并且通过 DPAPI,可以通过进程间通信解密数据。