Configuration File Encryption

Background


I got the task of setting up CI. It was decided to use the transformation of configuration files and store confidential data in encrypted form. You can encrypt and decrypt them using the Key Container.

Key container


Every Windows OS has sets of generated keys. The key is generated either on the account or on the machine. The keys generated by the machine can be viewed along this path C: \ ProgramData \ Microsoft \ Crypto \ RSA \ MachineKeys. This is where the key that we will create next will go.

Key creation


We start cmd from the administrator and switch to the directory with aspnet_regiis, I have C: \ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319

Execute the command

aspnet_regiis -pc "TestKey" -exp 

exp - is added so that you can export the key in the future
TestKey - the name of our Key Container

Key Export


Team

 aspnet_regiis -px "TestKey" :\TestKey.xml -pri 

TestKey - name Key Container
C: \ TestKey.xml - the path where the file will be exported
pri - add a private key to export

Import key


Team

 aspnet_regiis -pi "TestKey" :\TestKey.xml 

TestKey - name Key Container
C: \ TestKey.xml - the path from where the file will be exported

Setting Rights


In order for your application or IIS to work with key container, you need to configure rights for it.

This is done by the team

 aspnet_regiis -pa "TestKey" "NT AUTHORITY\NETWORK SERVICE" 

TestKey - name Key Container
NT AUTHORITY \ NETWORK SERVICE - who will be given access to the key

By default, IIS has ApplicationPoolIdentity for the pool.

The Microsoft documentation (see link 2) describes ApplicationPoolIdentity as:


Therefore, for IIS to be able to decrypt the config, Identity must be configured in the pool for the account, or you can select "NETWORK SERVICE" and give it rights.

Adding a section to config


 <configProtectedData defaultProvider="RsaProtectedConfigurationProvider"> <providers> <add name="CustomRsaProtectedConfigurationProvider" type="System.Configuration.RsaProtectedConfigurationProvider,System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" description="Uses RsaCryptoServiceProvider to encrypt and decrypt" keyContainerName="TestKey" cspProviderName="" useMachineContainer="true" useOAEP="false"/> </providers> </configProtectedData> 

Also key container and RsaProtectedConfigurationProvider are defined globally in files

C: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ Config \ machine.config, C: \ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319 \ Config \ machine.config

 <configProtectedData defaultProvider="RsaProtectedConfigurationProvider"> <providers> <add name="RsaProtectedConfigurationProvider" type="System.Configuration.RsaProtectedConfigurationProvider,System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" description="Uses RsaCryptoServiceProvider to encrypt and decrypt" keyContainerName="NetFrameworkConfigurationKey" cspProviderName="" useMachineContainer="true" useOAEP="false"/> <add name="DataProtectionConfigurationProvider" type="System.Configuration.DpapiProtectedConfigurationProvider,System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" description="Uses CryptProtectData and CryptUnProtectData Windows APIs to encrypt and decrypt" useMachineProtection="true" keyEntropy=""/> </providers> </configProtectedData> 

Encryption


Encryption itself can be done in three ways.

Command Line Encryption


 aspnet_regiis.exe -pef connectionStrings :\Site -prov "CustomRsaProtectedConfigurationProvider" 

C: \ Site - path to the file with the config.

CustomRsaProtectedConfigurationProvider - our provider specified in the config called key container.

Encryption through a written application


 private static string provider = "CustomRsaProtectedConfigurationProvider"; public static void ProtectConfiguration() { Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); ConfigurationSection connStrings = config.ConnectionStrings; if (connStrings != null && !connStrings.SectionInformation.IsProtected && !connStrings.ElementInformation.IsLocked) { connStrings.SectionInformation.ProtectSection(provider); connStrings.SectionInformation.ForceSave = true; config.Save(ConfigurationSaveMode.Full); } } public static void UnProtectConfiguration(string path) { Configuration config = ConfigurationManager.OpenExeConfiguration(path); ConfigurationSection connStrings = config.ConnectionStrings; if (connStrings != null && connStrings.SectionInformation.IsProtected && !connStrings.ElementInformation.IsLocked) { connStrings.SectionInformation.UnprotectSection(); } } 

Bicycle


When there is a file transformation and you need to encrypt sections separately from the entire config, then only a self-written version is suitable. We create an instance of the RsaProtectedConfigurationProvider class, take a node from xml and encrypt it separately, then replace the original xml node with our encrypted one and save the result.

 public void Protect(string filePath, string sectionName = null) { XmlDocument xmlDocument = new XmlDocument { PreserveWhitespace = true }; xmlDocument.Load(filePath); if (xmlDocument.DocumentElement == null) { throw new InvalidXmlException($"Invalid Xml document"); } sectionName = !string.IsNullOrEmpty(sectionName) ? sectionName : xmlDocument.DocumentElement.Name; var xmlElement = xmlDocument.GetElementsByTagName(sectionName)[0] as XmlElement; var config = new NameValueCollection { { "keyContainerName", _settings.KeyContainerName }, { "useMachineContainer", _settings.UseMachineContainer ? "true" : "false" } }; var rsaProvider = new RsaProtectedConfigurationProvider(); rsaProvider.Initialize(_encryptionProviderSettings.ProviderName, config); var encryptedData = rsaProvider.Encrypt(xmlElement); encryptedData = xmlDocument.ImportNode(encryptedData, true); var createdXmlElement = xmlDocument.CreateElement(sectionName); var xmlAttribute = xmlDocument.CreateAttribute("configProtectionProvider"); xmlAttribute.Value = _encryptionProviderSettings.ProviderName; createdXmlElement.Attributes.Append(xmlAttribute); createdXmlElement.AppendChild(encryptedData); if (createdXmlElement.ParentNode == null || createdXmlElement.ParentNode.NodeType == XmlNodeType.Document || xmlDocument.DocumentElement == null) { XmlDocument docNew = new XmlDocument { InnerXml = createdXmlElement.OuterXml }; docNew.Save(filePath); } else { xmlDocument.DocumentElement.ReplaceChild(createdXmlElement, xmlElement); xmlDocument.Save(filePath); } } 

References


1.docs.microsoft.com/en-us/previous-versions/53tyfkaw
2.support.microsoft.com/en-za/help/4466942/understanding-identities-in-iis

Source: https://habr.com/ru/post/462379/


All Articles