45

I want to securely store a plaintext password on Windows PC. I am currently using DPAPI CryptProtectData to encrypt it, then store the encrypted blob in a file in user's local AppData.

In Windows 7, there is Windows Vault, a credential manager (Control Panel\User Accounts and Family Safety\Credential Manager) that stores logon data for a variety of logon types, including "generic credential". On the surface this looks like the right place for a program to store credentials. However, I was not able to find any API for it. I read Authentication function reference in MSDN, but frankly got lost in it.

Is there an API to Windows Vault to store and retrieve credentials from a program, and, if yes, where can I find documentation?

Nathan Moinvaziri
  • 5,506
  • 4
  • 29
  • 30
  • 2
    Looks like CredWrite(), CredRead(), et al under the Credentials Management Functions section. – Luke Feb 10 '12 at 02:37
  • 1
    I'm currently looking at solutions for storing user's credentials -- could you tell me why you decided to switch from storing the encrypted blob from CryptProtectData to Windows Vault? More security? It seem's less portable to previous Windows versions (but at the moment, I'm not sure I will care). – BSchlinker Mar 26 '12 at 17:40
  • Like BSchlinker, I don't get what this is gaining us. How is this any more secure than storing on the filesystem? – Sámal Rasmussen Sep 06 '16 at 09:16
  • @Sammi: There is nowhere an implication that this method is more/less secure than any other. I do not think it is actually "more" secure in any sense, but I am not equipped to tell you for sure, sorry. – kkm inactive - support strike Sep 06 '16 at 20:50
  • @kkm Just seems pointless, was what I was thinking. Pain for no gain. But I dunno. Also it locks you to Windows, which is unfortunate in these days of cross platform .net core glory. – Sámal Rasmussen Sep 07 '16 at 07:38
  • http://stackoverflow.com/questions/17741424/retrieve-credentials-from-windows-credentials-store-using-c-sharp – Ohad Schneider Dec 19 '16 at 22:28

5 Answers5

58

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.

  • Is there a way to use the credential in a way that it cant be retrieved? (like only logging into a URL). – eckes Aug 30 '16 at 22:15
  • When you get a linker error you can add this line `#pragma comment(lib, "Advapi32.lib")` – rekire Jul 24 '22 at 13:36
  • @rekire I'll update. You see it in Windows 10, correct? They reshuffled locations of system exports significantly. Thanks for the #include fix, BTW! – kkm inactive - support strike Jul 27 '22 at 18:38
  • Yes I had this with Windows 10. Thank you for your answer I would never have managed with without it :-) – rekire Jul 28 '22 at 06:51
  • For anyone coming confused from Cygwin: The format specifiers `%s` and `%S` mean "same" and "opposite" character size relative to the format string (which is wide for `wprintf` and narrow for `printf`). In ANSI C/C++, `%s` always mean narrow and `%ls` always means wide, so use `%ls` and `%s`, instead of `%s` and `%S`, respectively. – Irfy Aug 15 '22 at 10:38
  • @Irfy thanks for the nitpick! You should have simply edited the answer. :) – kkm inactive - support strike Aug 16 '22 at 07:06
  • @irfy Aw shucks, it's not that simple as just replacing `%S` with `%ls`; it's the other way around! But recent MS compilers have been very standard-compliant. Is there a compiler switch for that? I'll try to figure out. – kkm inactive - support strike Aug 16 '22 at 07:18
18

For people joining the thread late, there is a new library to interact with this store in Windows 8 called: Windows.Security.Credentials.PasswordVault

In fact it only takes two lines of powershell to use the class to view all user names and passwords stored under the current users account:

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

Update: It looks like Microsoft (thankfully) restricted this api more in Windows 10 and it will no longer dump all your passwords so trivially. This is only indication of the change that I have seen:

The contents of the locker are specific to the app or service. Apps and services don't have access to credentials associated with other apps or services.

Tim
  • 412
  • 7
  • 18
  • @ClairelyClaire That would still be through credman. When I view my windows live credential through the gui in win8.1, it shows up as virtualapp/didlogical with a type of generic. So if you enumerate the creds of type cred.Type = CRED_TYPE_GENERIC you should find that one. [link](http://answers.microsoft.com/en-us/windows/forum/windows_7-security/unknown-credential-virtualappdidlogical/40467173-a75a-44b2-8617-5aa7a0479925) – Tim Jul 29 '14 at 03:16
  • I looked through the commands for the PowerShell script Tim Lewis posted - not sure how to use the above info with that. I can see the stored credential associated with the MS account connected to the Windows 8 user account - there's just no password. – CXL Aug 02 '14 at 23:56
  • Cannot follow this statement, as all passwords written to Win10 password vault with TYPE_GENERIC can be read also by other applications. In https://stackoverflow.com/questions/33754249/windows-credenumerate-in-jna/63427329#63427329 a sample is included that prints passwords from several applications. Only the Domain passwords are not (or not that easy) accessible, and WindowsLive and MicrosoftAccount passwords also can't be accessed (when I tried, the CredEnumerate returned an Error) – Christoph Bimminger Aug 15 '20 at 14:57
10

If anyone is interested in reading and writing to it from PowerShell or C#, here's a link to a script that does it:

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

The PowerShell script accesses the API via inline C# that utilizes Pinvoke.

Tim Lewis
  • 3,335
  • 1
  • 36
  • 26
  • link is down. do you have a current version? – jan-glx Oct 20 '21 at 13:41
  • 1
    Updated the link to use archive.org. This is due to [Microsoft closing the TechNet Gallery](https://learn.microsoft.com/en-us/teamblog/technet-gallery-retirement). Also found a copy of it here: https://github.com/zetlen/clortho/blob/master/CredMan.ps1 – Tim Lewis Oct 25 '21 at 15:53
1

For persons landing here and realising that the above answers regarding Windows Credential Vault no longer work as of .NET 7...

https://www.nuget.org/packages/CredentialVaultManager

to use:

var credential = new Credential();
credential.Target = "sometargetname";
if(credential.Exists()) {
   credential.Load();
}
user77232
  • 203
  • 3
  • 10
0

kkm's answer shows how to create generic credential. If you need to create Windows credentials, you can modify several parameters. The specific code example is as follows, hoping to help.

#include <windows.h>
#include <wincred.h>
#include <tchar.h>
#include <wchar.h>
#include <iostream>
#pragma hdrstop


int main()
{
    { 
        char* password = "mypassword";

        //DO NOT ADD 1
        DWORD blobSize = strlen(password);

        CREDENTIALW cred = { 0 };

        //cred.Type = CRED_TYPE_GENERIC;
        cred.Type = CRED_TYPE_DOMAIN_PASSWORD;

        //TargetName cannot be in the format "FOO/ Account"
        cred.TargetName = L"127.0.0.1";

        cred.CredentialBlobSize = blobSize;
        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"127.0.0.1", CRED_TYPE_DOMAIN_PASSWORD, 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);
    }
}
  • If the Type member is CRED_TYPE_DOMAIN_PASSWORD, this member contains the plaintext Unicode password for UserName. The CredentialBlob and CredentialBlobSize members do not include a trailing zero character.That's why blobsize isn't the length of password plus one. CREDENTIALA (wincred.h)- Win32 apps
  • If Type is CRED_TYPE_DOMAIN_PASSWORD or CRED_TYPE_DOMAIN_CERTIFICATE, this member identifies the server or servers that the credential is to be used for. The member is either a NetBIOS or DNS server name, a DNS host name suffix that contains a wildcard character, a NetBIOS or DNS domain name that contains a wildcard character sequence, or an asterisk.
qihao7777
  • 1
  • 2