How to store a password on Windows?
This post is part of the series 'Password management'. Be sure to check out the rest of the blog posts of the series!
- How to store a password in a web application?
- How to implement Password reset feature in a web application?
- How to store a password on Windows? (this post)
- How to prompt for a password on Windows?
- Helping users to create good passwords
- Automatically log in a user on a website using the Credential Management API?
- How to avoid storing secrets in the source code?
In the previous post, I wrote about storing a password 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 every time 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 safely? 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
Windows created using CredUIPromptForWindowsCredentials
The methods are not directly exposed in .NET, so you need some DllImport
s. 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, optional entropy is a way to prevent other applications 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 possible in .NET, it makes it a little more complex. This is why you should use an entropy for your application.
Do you have a question or a suggestion about this post? Contact me!