Leveraging the DPAPI to encrypt sensitive configuration settings - Jonathan Crozier (2023)

It is very common that an application will need to store settings in a configuration file and then later retrieve the value of those settings.

Quite often, one or more of these settings will be sensitive in nature; such as API keys, database connection strings, or perhaps mail server passwords.

Protecting sensitive data is a major concern for many applications and this concern should apply not only to data but also to the credentials used to access the data which tend to be stored in configuration files.

In this article, I explain what the DPAPI is and how it can be leveraged to encrypt sensitive configuration file settings on a Windows device.


If you’ve never heard of the DPAPI, let me clarify what it is before going any further.

DPAPI is an acronym for Data Protection Application Programming Interface.

So loosely speaking the DPAPI is an API that is all about protecting (encrypting) data.

The DPAPI has been around since the days of Windows 2000 (battle-tested!) in different forms and solves the hard problem of generating and storing the cryptographic keys required to encrypt and decrypt data.

The major quandary when encrypting data has always been where to store the encryption key.

The truth of the matter is that client devices cannot store a key/secret value in complete safety. However, the DPAPI can help us greatly as it allows us to delegate the responsibility for the management of the primary encryption key to the OS.

There are two main approaches when encrypting data using the DPAPI. You can choose to use one of the following two ‘Scopes’.

  • Current User
  • Local Machine

These Data Protection Scopes both offer strong protection for sensitive data, but they differ in how the encryption key is generated.

‘Current User’ Scope is the most secure method of encrypting data since the encryption key is based on the password of the logged-in user. It will only be possible to decrypt the data (when not logged into the OS) if you can establish the user’s password.

‘Local Machine’ Scope uses a key that is specific to the machine and therefore any data that is encrypted in this manner can be decrypted across different user profiles. This is very useful whenever you want your application to work across different user accounts without needing to reconfigure settings for each account.

You can read more about the nature and usage of the DPAPI within the Microsoft Docs.

Using the DPAPI

Now let’s see how we can use the DPAPI from a .NET project to encrypt and decrypt data.

All of the code used in this article works in both .NET and .NET Core. However, if you’re using .NET Core, you’ll need to install the System.Security.Cryptography.ProtectedData NuGet package in order for the code below to compile.

Note that I have created a sample project on GitHub and the helper methods from the following two sub-sections can be found within a class named EncryptionProvider.


First of all, let’s look at how to encrypt some text using the DPAPI.

///<summary>///Encryptsthespecifiedcleartext.///</summary>///<paramname="clearText">Thecleartexttoencrypt</param>///<paramname="entropy">Optionalentropykey</param>///<returns>Theencryptedtext</returns>publicstaticstringEncrypt(stringclearText,byte[]entropy){if(clearText==null)thrownewArgumentNullException(nameof(clearText)); byte[]clearBytes=Encoding.UTF8.GetBytes(clearText);byte[]encryptedBytes=ProtectedData.Protect(clearBytes,entropy,DataProtectionScope.LocalMachine); returnConvert.ToBase64String(encryptedBytes);}

The Encrypt method defined above takes the clear text that you want to encrypt as a parameter and converts that into a UTF8 byte array. The bytes are then passed to the static Protect method of the ProtectedData class (this is the .NET wrapper class for accessing the DPAPI) which returns a new byte array containing the encrypted data. Lastly, the encrypted bytes are converted to a Base64 string.

By using UTF8 encoding when generating the bytes to encrypt, we ensure that all of the text is encoded correctly whenever languages that have extended character sets are being used.

By using Base64 encoding for the final encrypted text, we ensure that we are able to store the encrypted data in a consistent manner, without any special characters which could interfere with serialization or other data storage concerns.

You’ll notice that in the example I am using LocalMachine Scope so that the encrypted data can be accessed across different user profiles. You can also specify CurrentUser Scope if you only want it to be possible for the data you are encrypting to be decrypted by the currently logged in user.

Additionally, you may be wondering what the entropy parameter is for. The ‘entropy’ value is optional but if it is not specified, it will be possible for other applications running on the same machine to decrypt the data you’ve encrypted if they call into the DPAPI. By specifying an entropy value, which is essentially a secondary key, you can mitigate against this possibility and provide some assurance that the data is not only protected from humans but also from other system applications.


Next, let’s look at how to decrypt some text using the DPAPI.

///<summary>///Decryptsthespecifiedcleartext.///</summary>///<paramname="encryptedText">Theencryptedtexttodecrypt</param>///<paramname="entropy">Optionalentropykey</param>///<returns>Thedecryptedtext</returns>publicstaticstringDecrypt(stringencryptedText,byte[]entropy){if(encryptedText==null)thrownewArgumentNullException(nameof(encryptedText));byte[]encryptedBytes=Convert.FromBase64String(encryptedText);byte[]clearBytes=ProtectedData.Unprotect(encryptedBytes,entropy,DataProtectionScope.LocalMachine); returnEncoding.UTF8.GetString(clearBytes);}

The decryption code is very similar to the encryption code. It is essentially doing the inverse of each operation; converting the Base64 encrypted text to a byte array, passing this to the static Unprotect method of the ProtectedData class and then converting the returned bytes into a UTF8 string.

Again I am using the LocalMachine Data Protection Scope and passing along the entropy value.

If the specified entropy value does not match the entropy which was used when the data was encrypted, a decryption error will be thrown.

Encrypted settings

Now that we have an understanding of how to utilise the DPAPI to encrypt and decrypt data, let’s look at how we can use the encryption logic we’ve created to encrypt configuration file settings.

Note that within the sample project on GitHub you can find the helper methods from the following sub-sections within a class named EncryptionSettings(unless specified otherwise).


Before looking at the code let’s create a configuration file that will be used to store the encrypted settings.

In .NET Core, it is common practice to use an appsettings.json file. However, in my example, I’m going to use an XML-based App.config file which will work with Framework support in both .NET and in .NET Core (with the help of the System.Configuration.ConfigurationManager NuGet package).

Below are the contents of an example ‘App.config’ file which can be used to store encrypted settings.


The above XML is a standard .NET configuration file with a configuration root element.

A custom ‘encryptedSettings’ section is defined within the configSections element. If you are already familiar with .NET configuration files, you’ll notice that the type has been set to ‘AppSettingsSection’ and therefore this custom section will work in the same manner as a standard ‘appSettings’ section.

Within the custom encryptedSettings element I have defined a single encrypted setting key and value which will be accessed from the sample code.

Getting encrypted settings

Now that we have the App.config file set up, we can access the settings defined within the ‘encryptedSettings’ configuration section from our code, as follows.


Note that I recommend using a constant value for the sectionName parameter which is passed to the GetSection method, as per my sample project on GitHub.

The above method will return a NameValueCollection object which is essentially a key-value list of the encrypted settings, where the setting values can be accessed by their key name.

Determining encryption status

It is important that our code is able to differentiate plain values from encrypted values.


Note that I recommend using a constant value for the ‘CipherValue:’ string used above, as per the sample project on GitHub.

The above method simply checks if the specified text starts with the encrypted text prefix identifier that I’ve chosen.

Encrypting settings

Now let’s look at how to encrypt a configuration file setting.

The helper method below can be used to encrypt a specific setting within the ‘encryptedSettings’ section of the App.config file.

///<summary>///Encryptsthevalueforthespecifiedkey.///</summary>///<paramname="key">Thekey/nameoftheencryptedsetting</param>///<paramname="value">Thevaluetosetfortheencryptedsetting</param>privatestaticvoidEncryptSetting(stringkey,objectvalue){//Opentheconfigurationfileandsettheencryptedvalueforthespecifiedsettingkey.Configurationconfiguration=ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);AppSettingsSectionencryptedSection=configuration.GetSection("encryptedSettings")asAppSettingsSection;encryptedSection.Settings[key].Value="CipherValue:"+EncryptionProvider.Encrypt(Convert.ToString(value),Settings.Entropy); //Savechangestotheconfigurationfile.configuration.Save(ConfigurationSaveMode.Modified); //RefreshtheEncryptedSettingssectionwithintheconfiguration//filetoupdatetheconfigurationdetailsin-memory.ConfigurationManager.RefreshSection("encryptedSettings");}

Note that I recommend using constant values for the string values used above, as per the sample project on GitHub. Settings.Entropy is a byte array containing a demo key for the entropy value.

In short, the above code opens the application configuration (App.config) file and gets the ‘encryptedSettings’ section. The specified setting is then accessed by its key and the value is set to the text ‘CiperValue:’ (so that we can identify encrypted data later) plus the encrypted string. The configuration file is then saved and the ‘encryptedSettings’ section is refreshed to make sure we have the latest setting values in memory whenever they are next accessed from our code.

We can create a public method to set an encrypted setting, as per the code below.


The code simply passes the key and value parameters to the EncryptSetting method.

Decrypting settings

Next, let’s look at how to decrypt a configuration file setting.

///<summary>///Getsthedecryptedvalueforthespecifiedencryptedsettingkey.///Automaticallyencryptsthesettingifitisnotalreadyencrypted.///</summary>///<paramname="key">Thekey/nameoftheencryptedsetting</param>///<returns>Thedecryptedvalueasastring</returns>publicstaticstringGet(stringkey){stringvalue=GetEncryptedSettings()[key]; //There'snoneedtodecrypt/encryptemptyvalues.if(string.IsNullOrEmpty(value))returnvalue; if(EncryptionProvider.IsEncrypted(value)){//Gettheencrypteddatafromthevalue(i.e.stripoutthe'CipherValue:'prefix).value=value.Substring("CipherValue:".Length,value.Length-"CipherValue:".Length); //Decryptthedata.returnEncryptionProvider.Decrypt(value,Settings.Entropy);}else{//Ifthesettingisnotalreadyencrypted,encryptitbeforereturningthedecrypteddata.EncryptSetting(key,value); returnvalue;}}

Note that I recommend using constant values for the string values used above, as per my sample project on GitHub. Settings.Entropy is a byte array containing a demo key for the entropy value.

The code above gets the value of the specified setting using the GetEncryptedSettings method we defined earlier to access the setting by its key.

If the value of the setting is empty then there is nothing to decrypt or encrypt so we simply return the value.

Next, we check if the value is already encrypted.

If the value is encrypted we get a substring from the text, stripping out the ‘CipherValue:’ constant which is used to indicate that the value is encrypted. The value is then decrypted and returned as a string.

If the value is not already encrypted we encrypt it and then return it as a string.


Now let’s demonstrate how we can use the methods defined within the EncryptedSettings class to retrieve and store encrypted configuration file settings.

First up, let’s get the value of an encrypted setting.


The value returned in my example is as follows.

This text will be encrypted.

The first time we get a value for a setting, the EncryptedSettings class takes care of ensuring that the setting has been encrypted before returning its value.

The value of the setting in the App.config file (in the bin\Debug folder) will look something like the following.


Now on to setting the value of an encrypted setting.


As you can see from the above code, to update an encrypted setting it is simply a case of passing the appropriate encrypted setting key and the new value to store.


In summary, we can use the DPAPI to do the heavy lifting for us and take care of the difficult problem of taking care of the storage of cryptographic keys.

The .NET Framework provides a convenient way of using the DPAPI via the ProtectedData class.

There are plenty of possibilities to further improve or alter the code included in this article to suit your specific needs.

For example, you may wish to change the Data Protection Scope from ‘LocalMachine’ to ‘CurrentUser’, or perhaps abstract the concept of the ‘EncryptionProvider’ further by introducing an interface so that different methods of encryption can be supported.

You can view and download the code used in this article from the sample repository on GitHub.

I hope you enjoyed this post! Comments are always welcome and I respond to all questions.

If you like my content and it helped you out, please check out the button below 🙂


Top Articles
Latest Posts
Article information

Author: Jeremiah Abshire

Last Updated: 01/30/2023

Views: 6053

Rating: 4.3 / 5 (54 voted)

Reviews: 85% of readers found this page helpful

Author information

Name: Jeremiah Abshire

Birthday: 1993-09-14

Address: Apt. 425 92748 Jannie Centers, Port Nikitaville, VT 82110

Phone: +8096210939894

Job: Lead Healthcare Manager

Hobby: Watching movies, Watching movies, Knapping, LARPing, Coffee roasting, Lacemaking, Gaming

Introduction: My name is Jeremiah Abshire, I am a outstanding, kind, clever, hilarious, curious, hilarious, outstanding person who loves writing and wants to share my knowledge and understanding with you.