【问题标题】:How do I store and retrieve credentials from the Windows Vault credential manager?如何从 Windows Vault 凭据管理器存储和检索凭据?
【发布时间】:2012-03-02 12:52:37
【问题描述】:

我想在 Windows PC 上安全地存储明文密码。我目前正在使用 DPAPI CryptProtectData 对其进行加密,然后将加密的 blob 存储在用户本地 AppData 的文件中。

在 Windows 7 中,Windows Vault 是一个凭据管理器(控制面板\用户帐户和家庭安全\凭据管理器),用于存储各种登录类型的登录数据,包括“通用凭据”。从表面上看,这看起来像是程序存储凭据的正确位置。但是,我找不到任何 API。我在 MSDN 中阅读了 Authentication function 参考资料,但坦率地说,我迷路了。

是否有用于 Windows Vault 的 API 来存储和检索程序中的凭据,如果有,我在哪里可以找到文档?

【问题讨论】:

  • 在凭证管理功能部分下看起来像 CredWrite()、CredRead() 等。
  • 我目前正在寻找存储用户凭据的解决方案——您能告诉我为什么您决定将加密的 blob 从 CryptProtectData 存储到 Windows Vault 吗?更安全?它似乎不太适合以前的 Windows 版本(但目前,我不确定我是否会在意)。
  • 像 BSchlinker 一样,我不明白这给我们带来了什么。这怎么比存储在文件系统上更安全?
  • @Sammi:没有任何地方暗示这种方法比任何其他方法更安全/更不安全。我不认为它实际上在任何意义上都“更”安全,但我没有能力肯定地告诉你,抱歉。
  • @kkm 看起来毫无意义,这就是我的想法。苦无所得。但我不知道。它还将您锁定在 Windows 上,这在跨平台 .net 核心荣耀的时代是不幸的。

标签: security winapi authentication credentials dpapi


【解决方案1】:

非常感谢@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);
        // must free memory allocated by CredRead()!
        ::CredFree (pcred);
    }
}

通用凭据存储在 Windows Vault 中,如屏幕截图所示:

附录:Vault vs Crypto DP API

这个答案似乎很受欢迎,自从我写下它以来,近 6 年来一直定期投票。 cmets 中提出了关于将凭据存储在保险库中与使用::CryptProtectData() API 加密凭据 blob 并随时存储之间的区别的问题。以下是我对主要区别的理解,可能并非详尽无遗。

  • 漫游控制。 Vault 中的存储由系统管理。在域环境中,设置cred.Persist = CRED_PERSIST_ENTERPRISE; 使加密凭据成为用户漫游配置文件的一部分,因此可供登录任何域计算机的用户使用。 CryptProtectData() 只加密数据;密文的保存是用户的责任。在%APPDATA% 下存储密文可能也会使其漫游,但setting a proper ACL on the fileenforcing encryption-at-rest with the EFS 再次是调用者的责任。保管库数据由系统静态加密。
  • UI 可见性。 Vault 凭据显示在 Vault UI 中,当不再需要或怀疑被泄露时,可能会被撤销。从CryptProtectData() 获得的密文完全由应用程序控制。在目标软件设计中必须考虑可见性特征。
  • Vault 支持不稳定的每次登录会话机密,加密存储在内存中 (cred.Persist = CRED_PERSIST_SESSION;)。使用通用 API 实现此类功能相对较难,因为它涉及经过身份验证的 IPC 或具有同步的正确保护的共享内存映射等。
  • 盐腌。在CryptProtectData() 的情况下,调用者可能会提供额外的盐(必须在解密期间再次提供,因此有办法存储它)。保险柜会在内部处理它。
  • 保险柜的范围更窄。使用 Vault 存储与身份无关的数据可能是一种设计风格。
  • 审核。 CryptProtectData() 可以控制在 blob 被解密时创建审计记录(CRYPTPROTECT_AUDIT 位标志)。我在 Vault API (wincred.h) 中看不到类似的东西。我不知道是否可以审计 Vault 访问;如果它总是由 GPO 完成、从未完成或由 GPO 控制;事实上,我在这里画了一个空白。
  • 保险库受HVCI (née Device Guard保护;仅在 Windows 10 专业版和企业版以及相应的服务器 SKU 中可用)。启用后,系统的受保护部分在单独的半虚拟化、硬件支持、严格控制的地址空间中运行,该地址空间在常规地址空间中根本“不存在”(HVCI 保护空间是 LSA 和其他关键组件的位置)也住)。这使得即使从内核模式堆栈也无法访问它。虽然 CNG 提供者也存在于该隔间中,但当密文 blob 被交回调用程序时,CryptProtectData() 的结果必然跨越此边界(另外,我不确定CryptProtectData() 是否由 CNG 支持)。 Vault 加密的数据保持在受保护的边界内;只有明文穿过它。

总之,Vault 是一个更高级别、目标明确的 API,用于保存用户可见、用户管理的凭据和其他与身份相关的秘密,通过系统 UI 进行管理。 CryptProtectData() 是一种通用加密 API,具有更大的灵活性,需要编写和审核更多代码才能安全地管理持久密文。

两者中哪个“更安全”的问题是不恰当的。没有“或多或少安全”的定义可以适用于所有加密的使用。

【讨论】:

  • 有没有办法以无法检索的方式使用凭证? (就像只登录一个 URL)。
【解决方案2】:

如果有人有兴趣从 PowerShell 或 C# 读取和写入它,这里是执行此操作的脚本的链接:

PowerShell Credentials Manager: CredMan.ps1 (via archive.org)

PowerShell 脚本通过使用 Pinvoke 的内联 C# 访问 API。

【讨论】:

【解决方案3】:

对于较晚加入线程的人,在 Windows 8 中有一个新库可以与此商店进行交互,名为:Windows.Security.Credentials.PasswordVault

实际上只需要两行powershell就可以使用该类查看当前用户帐户下存储的所有用户名和密码:

[void][Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]
(new-object Windows.Security.Credentials.PasswordVault).RetrieveAll() | % { $_.RetrievePassword(); $_ }

更新: 看起来微软(谢天谢地)在 Windows 10 中更多地限制了这个 api,它不会再如此琐碎地转储你所有的密码。这只是我看到的变化的迹象:

储物柜的内容特定于应用或服务。应用和服务无权访问与其他应用或服务关联的凭据。

【讨论】:

  • @ClairelyClaire 那仍然是通过 credman。当我通过 win8.1 中的 gui 查看我的 windows 实时凭据时,它显示为具有通用类型的 virtualapp/didlogical。因此,如果您枚举 cred.Type = CRED_TYPE_GENERIC 类型的凭据,您应该找到那个。 link
  • 我查看了 Tim Lewis 发布的 PowerShell 脚本的命令 - 不知道如何使用上述信息。我可以看到与连接到 Windows 8 用户帐户的 MS 帐户关联的存储凭据 - 只是没有密码。
  • 不能遵循此声明,因为所有使用 TYPE_GENERIC 写入 Win10 密码库的密码也可以被其他应用程序读取。在stackoverflow.com/questions/33754249/… 中包含一个示例,该示例打印来自多个应用程序的密码。只有域密码无法(或不太容易)访问,并且 WindowsLive 和 MicrosoftAccount 密码也无法访问(当我尝试时,CredEnumerate 返回了错误)
猜你喜欢
  • 1970-01-01
  • 2013-07-18
  • 1970-01-01
  • 2020-02-15
  • 1970-01-01
  • 2015-04-12
  • 2011-11-02
  • 1970-01-01
  • 2015-12-09
相关资源
最近更新 更多