非常感谢@Luke 的提示:将凭据存储到Windows Vault 并从中读取它们的Windows API 函数是CredWrite()
和CredRead()
. 这是一个可以编译和运行的代码示例,我用来确认这些函数确实做了预期的事情:
#include <windows.h>
#include <wincred.h>
#include <tchar.h>
#pragma hdrstop
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 vs Crypto DP API
答案似乎很受欢迎,自从我写它以来,近 6 年来一直定期投票。评论中提出了关于将凭证存储在保险库中与使用::CryptProtectData()
API 加密凭证 blob 并在任何时候存储它之间的区别的问题。以下是我对关键差异的理解,可能并非详尽无遗。
- 漫游控制。Vault 中的存储由系统管理。在域环境中,设置
cred.Persist = CRED_PERSIST_ENTERPRISE;
使加密凭据成为用户漫游配置文件的一部分,因此可供登录任何域计算机的用户使用。CryptProtectData()
只加密数据;密文的保存是用户的责任。根据域的漫游设置,存储密文%APPDATA%
也可能使其漫游,但在文件上设置适当的 ACL并使用 EFS 执行静态加密再次是调用者的责任。保险库数据由系统静态加密。
- 用户界面可见性。Vault 凭据显示在 Vault UI 中,当不再需要或怀疑被泄露时,可能会被撤销。从中获得的密文
CryptProtectData()
完全由应用程序控制。在目标软件设计中必须考虑可见性特征。
- Vault 支持不稳定的每次登录会话机密,加密存储在内存中 (
cred.Persist = CRED_PERSIST_SESSION;
)。使用通用 API 实现此类功能相对较难,因为它涉及经过身份验证的 IPC 或具有同步的正确保护的共享内存映射等。
- 盐。在 的情况下
CryptProtectData()
,调用者可能会提供额外的盐(必须在解密期间再次提供,因此有办法存储它)。Vault 在内部处理它。
- 保险柜的范围更窄。使用 Vault 存储与身份无关的数据可能是一种设计味道。
- 审计。
CryptProtectData()
可以控制在解密 blob 时创建审计记录(CRYPTPROTECT_AUDIT
位标志)。我在 Vault API ( ) 中看不到类似的东西wincred.h
。我不知道是否可以审计 Vault 访问;如果它总是由 GPO 完成、从未完成或由 GPO 控制;事实上,我在这里画了一个空白。
- Vault 受HVCI 保护(née Device Guard;仅在 Windows 10/11 Pro 和 Enterprise 以及相应的服务器 SKU 中可用)。启用后,系统的受保护部分在单独的半虚拟化、硬件支持、严格控制的地址空间中运行,该地址空间在常规地址空间中根本“不存在”(HVCI 保护空间是 LSA 和其他关键组件的位置)也住)。这使得即使从内核模式堆栈也无法访问它。虽然 CNG 提供者也存在于该隔间中,
CryptProtectData()
但当密文 blob 被交回调用程序时,结果必然跨越此边界(此外,我不确定是否CryptProtectData()
由 CNG 支持)。Vault 加密的数据保持在受保护的边界内;只有明文穿过它。
总之,Vault 是一个更高级别、目标明确的 API,用于保存用户可见、用户管理的凭据和其他与身份相关的机密,通过系统 UI 进行管理。CryptProtectData()
是一种通用的加密 API,具有更大的灵活性,需要编写和审核更多代码才能安全地管理持久密文。
两者中哪一个“更安全”的问题是不恰当的。没有“或多或少安全”的定义可以适用于所有加密的使用。