非常感谢 @Luke 的提示:用于存储凭据并从 Windows Vault 读取凭据的 Windows API 函数是CredWrite() and CredRead()。这是一个可以编译和运行的代码示例,我用它来确认这些函数确实做了预期的事情:
#include <windows.h>
#include <wincred.h>
#include <wchar.h>
#pragma hdrstop
#pragma comment(lib, "advapi32.lib") // Or pass it to the cl command line.
int main ()
{
{ //--- SAVE
char* password = "brillant";
DWORD cbCreds = 1 + strlen(password);
CREDENTIALW cred = {0};
cred.Type = CRED_TYPE_GENERIC;
cred.TargetName = L"FOO/account";
cred.CredentialBlobSize = cbCreds;
cred.CredentialBlob = (LPBYTE) password;
cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
cred.UserName = L"paula";
BOOL ok = ::CredWriteW (&cred, 0);
wprintf (L"CredWrite() - errno %d\n", ok ? 0 : ::GetLastError());
if (!ok) exit(1);
}
{ //--- RETRIEVE
PCREDENTIALW pcred;
BOOL ok = ::CredReadW (L"FOO/account", CRED_TYPE_GENERIC, 0, &pcred);
wprintf (L"CredRead() - errno %d\n", ok ? 0 : ::GetLastError());
if (!ok) exit(1);
wprintf (L"Read username = '%s', password='%S' (%d bytes)\n",
pcred->UserName, (char*)pcred->CredentialBlob, pcred->CredentialBlobSize);
// Memory allocated by CredRead() must be freed!
::CredFree (pcred);
}
}
通用凭据存储在 Windows Vault 中,如屏幕截图所示:
附录:Vault 与 Crypto DP API
这个答案似乎很受欢迎,自从我写它以来近 6 年里,它经常被点赞。评论中提出了关于在保险库中存储凭证和使用以下方法加密凭证 blob 之间的区别的问题:::CryptProtectData()API 并随时存储它。这是我对主要差异的理解,可能并非详尽无遗。
- 漫游控制。 Vault 中的存储由系统管理。在域环境下,设置cred.Persist = CRED_PERSIST_ENTERPRISE;使加密凭据成为用户漫游配置文件的一部分,从而可供登录到任何域计算机的用户使用。
CryptProtectData()
仅加密数据;密文的保存是用户的责任。将密文存储在%APPDATA%
也可能使其漫游,具体取决于域的漫游设置,但是在文件上设置正确的 ACL and 使用 EFS 强制执行静态加密再次强调,这是调用者的责任。 Vault 数据由系统静态加密。
- 用户界面可见性。 Vault 凭证显示在 Vault UI 中,当不再需要或怀疑被泄露时,可能会被撤销。密文获取自
CryptProtectData()
完全由应用程序控制。在目标软件设计中必须考虑可见性功能。
- Vault 支持易失性的每个登录会话机密,加密存储在内存中(
cred.Persist = CRED_PERSIST_SESSION;
)。使用通用 API 实现此类功能相对较难,因为它涉及经过身份验证的 IPC 或具有同步功能的正确保护的共享内存映射等。
- 盐。的情况下
CryptProtectData()
,调用者可以提供额外的盐(在解密过程中必须再次提供,因此有办法存储它)。 Vault 在内部处理它。
- Vault 的范围较窄。使用 Vault 存储与身份无关的数据可能是一种设计味道。
- Audit.
CryptProtectData()
可以控制在解密 Blob 时创建审核记录(CRYPTPROTECT_AUDIT
位标志)。我在 Vault API 中看不到类似的内容(wincred.h
)。我不知道是否可以审核 Vault 访问;是否总是完成、从未完成或由 GPO 控制;事实上,我在这里画了一个空白。
- 保险库受保护HVCI(née设备卫士;仅适用于 Windows 10/11 专业版和企业版以及相应的服务器 SKU)。启用后,系统的受保护部分在单独的半虚拟化、硬件支持、严格控制的地址空间中运行,该地址空间在常规地址空间中根本“不存在”(HVCI 保护的空间是 LSA 和其他关键组件的所在位置)也住)。这使得即使从内核模式堆栈也无法访问它。虽然 CNG 供应商也住在那个隔间里,但结果是
CryptProtectData()
当密文 blob 交回调用程序时,必然会跨越此边界(另外,我不确定是否CryptProtectData()
是否有 CNG 支持)。保险库加密的数据保留在受保护的边界内;只有明文穿过它。
总之,Vault 是一个更高级别的、目标狭窄的 API,用于保存用户可见、用户管理的凭据和其他与身份相关的机密,并通过系统 UI 进行管理。CryptProtectData()
是通用加密 API,具有更大的灵活性,需要编写和审核更多代码才能安全地管理持久密文。
两者中哪一个“更安全”的问题是不恰当的。没有“或多或少安全”的定义可以适用于所有加密的使用。