How to store a password on Windows?

  • .NET
  • Security

This post is part of the series 'Password management'. Be sure to check out the rest of the blog posts of the series!

In the previous post, I wrote about storing a password in order to be able to authenticate a user. In this post the idea is to store a password on the local machine to avoid entering it every time. For instance, you don't want to always enter your credentials everytime you use OneNote. So, you need to save the password and be able to read it later. If you remind my previous post about cryptography, you know you have to encrypt it. But how to do that in a safe manner? Let's see some API of Windows to do that!

Windows Desktop application

Windows comes with a credential manager. It's not a well-known feature but it's very handy and easy to use. It allows to save secrets by encrypting them using the current user account, so only the current user can decrypt them. The complexity of encryption/decryption is abstracted. Multiple applications can access the persisted credentials, so you can implement a kind of Single Sign-on if you have multiple applications that access the same service. Last but not least, the user can manage their credential using the UI of the Credential Manager.

Windows Credential Manager

The credential manager allows you to store generic credentials. A generic credential is composed of an application name, a username, and a password. You can add/edit/delete credentials using the GUI or using the API. The API is composed of 3 main methods: CredRead, CredWrite, CredDelete. There are also methods for prompting password, but that will be for the next post: CredUIPromptForWindowsCredentials, CredUICmdLinePromptForCredentials

CredUIPromptForWindowsCredentials

The methods are not directly exposed in .NET, so you need some DllImports. You'll find the complete code on my GitHub repository. There is a NuGet package Meziantou.Framework.Win32.CredentialManager

First, you need to install the NuGet package Meziantou.Framework.Win32.CredentialManager (NuGet). Then, you can use the following code to save/read/delete credentials:

// Save the credential to the credential manager
CredentialManager.WriteCredential(
    applicationName: "CredentialManagerTests",
    userName: "meziantou",
    secret: "Pa$$w0rd",
    comment: "Test",
    persistence: CredentialPersistence.LocalMachine);

// Get a credential from the credential manager
var cred = CredentialManager.ReadCredential(applicationName: "CredentialManagerTests");
Console.WriteLine(cred.UserName);
Console.WriteLine(cred.Password);

// Delete a credential from the credential manager
CredentialManager.DeleteCredential(applicationName: "CredentialManagerTests");

UWP application or Desktop application on Windows 10

UWP exposes methods to use the credential manager. PasswordVault can be used directly in C#, JavaScript, or C++. The principle is the same except the credentials are only accessible by the current application.

// Save the credential to the credential manager
var vault = new Windows.Security.Credentials.PasswordVault();
var credential = new Windows.Security.Credentials.PasswordCredential(
    resource: "Sample Application",
    userName: "meziantou",
    password: "Pa$$w0rd");
vault.Add(credential);

// Get a credential from the credential manager
try
{
    var credential = vault.FindAllByResource(resourceName).FirstOrDefault();
    if (credential != null)
    {
        // Get the password
        return vault.Retrieve(credential.Resource, credential.UserName);
    }
}
catch
{
    // Throw an exception if no entries exist for the given resource
    // vault.Retrieve may also throw if the entry is deleted between vault.FindAllByResource and vault.Retrieve
    // More information about exceptions: https://github.com/MicrosoftDocs/winrt-api/issues/519#issuecomment-423780160
}

// Delete a credential from the credential manager
try
{
   PasswordCredential passwordCredential = vault.Retrieve(resourceName, userName);
   vault.Remove(passwordCredential);
}
catch
{
    // Throw an exception if no entries exist for the given resource
}

Data Protection API

The Data Protection API (DPAPI) is an API provided by Windows to encrypt and decrypt data using the user or machine credentials. If you go with this method, you will have to handle where you store the protected data. You can store it in a file for instance. The framework exposes this API through System.Security.Cryptography.ProtectedData. There are 2 static methods: Protect and Unprotect.

var entropy = new byte[] { 0, 1, 2, 3 };
var secret = Encoding.UTF8.GetBytes("Pa$$w0rd");

// Encrypt the data
byte[] protectedData = ProtectedData.Protect(secret, entropy, DataProtectionScope.CurrentUser);

// Encrypt the data
try
{
    byte[] clearData = ProtectedData.Unprotect(protectedData, entropy, DataProtectionScope.CurrentUser);
    string decryptedPassord = Encoding.UTF8.GetString(clearData);
}
catch (CryptographicException)
{
    // The entropy is different from the one used for encryption
    // The data was not encrypted by the current user (scope == CurrentUser)
    // The data was not encrypted on this machine (scope == LocalMachine)
}

Any application running with the current user can decrypt the data protected using this API. So, the entropy, which is optional, is a way to prevent other application from decrypting the data protected by your application. They'll have to know the entropy vector of your application. This means they need to decompile the application. While this is totally possible in .NET, it makes it a little more complex. This is why you should use an entropy for your application.

Follow me:
Enjoy this blog?Buy Me A CoffeeDonate with PayPal

Leave a reply