40

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

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

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

4

3 回答 3

48

非常感谢@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 中,如屏幕截图所示:

存储在 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,具有更大的灵活性,需要编写和审核更多代码才能安全地管理持久密文。

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

于 2012-02-10T12:53:05.260 回答
18

对于较晚加入线程的人,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,它不会再如此琐碎地转储你所有的密码。这只是我看到的变化的迹象:

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

于 2013-12-20T02:32:14.190 回答
10

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

PowerShell 凭据管理器:CredMan.ps1(通过 archive.org)

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

于 2013-08-29T13:46:58.133 回答