How do I store and retrieve credentials from the Windows Vault credential manager?

Many thanks to @Luke for the hint: Windows API functions to store credentials to and read them from Windows Vault are CredWrite() and CredRead(). Here is a code sample that may be compiled and run, that I used to confirm that these functions indeed do the expected thing:

#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);
    }
}

A generic credential is stored in Windows Vault, as can be seen on the screenshot:

A generic credential stored in Windows Vault

Addendum: Vault vs Crypto DP API

The answer appears to be quite popular, and is upvoted regularly for nearly 6 years since I wrote it. There were questions raised in the comments about the difference between storing credentials in the vault and encrypting a credential blob with the ::CryptProtectData() API and storing it whenever one pleases. Here’s my understanding, possibly non-exhaustive, of the key differences.

  • Roaming control. Storage in the Vault is managed by the system. In a domain environment, setting cred.Persist = CRED_PERSIST_ENTERPRISE; makes the encrypted credential part of the user’s roaming profile, and thus available to the user logged on any domain computer. CryptProtectData() only encrypts data; the keeping of the ciphertext is the user’s responsibility. Storing ciphertext under %APPDATA% possibly makes it roaming also, depending on the domain’s roaming setup, but setting a proper ACL on the file and enforcing encryption-at-rest with the EFS is, again, the caller’s responsibility. Vault data are encrypted at rest by the system.
  • UI visibility. A Vault credential is shown in the Vault UI, and may be revoked when no longer needed or suspected to be compromised. Ciphertext obtained from CryptProtectData() is fully controlled by the application. The visibility feature must be taken into account in the target software design.
  • Vault supports volatile per-logon-session secrets, stored encrypted in memory (cred.Persist = CRED_PERSIST_SESSION;). An implementation of such a feature with the generic API is relatively hard to get right, as it involves either authenticated IPC or correctly protected shared memory mapping with synchronization, etc.
  • Salting. In case of CryptProtectData(), the caller may provide additional salt (that has to be provided again during decryption, thus have means to store it). Vault takes care of it internally.
  • Vault has a narrower scope. Using Vault for storing of non-identity-related data is likely a design smell.
  • Audit. CryptProtectData() has control over creating an audit record when a blob is decrypted (the CRYPTPROTECT_AUDIT bit flag). I cannot see anything like this in the Vault API (wincred.h). I do not know whether auditing Vault access is possible; if it’s always done, never done, or controlled by a GPO; in fact, I’m drawing a blank here.
  • Vault is protected by HVCI (née Device Guard; available in Windows 10/11 Pro and Enterprise only, and corresponding Server SKUs). When enabled, the protected part of system runs in a separate paravirtualized, hardware-supported, tightly-controlled address space, which simply “does not exist” in the regular address space (the HVCI-protected space is where the LSA and other critical components also live). This makes it inaccessible even from a kernel mode stack. While CNG providers also live in that compartment, the result of CryptProtectData() necessarily crosses this boundary when the ciphertext blob is handed back to the calling program (also, I’m not sure if CryptProtectData() is backed by CNG or not). Vault-encrypted data stay within the protected boundary; only cleartext crosses it.

In summary, Vault is a higher-level, narrowly-targeted API for keeping user-visible, user-managed credentials and other identity-related secrets, managed through the system UI. CryptProtectData() is a general use encryption API, with more flexibility and more code needed to be written and audited to manage persisted ciphertext safely.

The question which of the two is “more secure” is ill-posed. There is no definition of “more or less secure” that could apply to any use of encryption across the board.

Leave a Comment