Хабрахабр

[Из песочницы] Шифрование конфигурационных файлов

Предыстория

Мне поступила задача по настройке CI. Было принято решение использовать трансформацию конфигурационных файлов и конфиденциальные данные хранить в зашифрованном виде.
Изучив документацию по шифрованию, вот что было сделано.

Key Container

В каждой ОС Windows есть наборы сгенерированных ключей. Ключ генерируется либо на учетную запись, либо на машину. Ключи сгенерированные на машину можно посмотреть по этому пути C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys. Сюда и отправиться ключ который мы создадим далее.

Создание ключа

Запускаем cmd от администратора и переключаемся в директорию с aspnet_regiis, у меня это C:\Windows\Microsoft.NET\Framework64\v4.0.30319

Выполняем команду

aspnet_regiis -pc "TestKey" -exp

exp — добавляется чтобы можно было экспортировать ключ в дальнейшем
TestKey — название нашего Key Container

Экспорт ключа

Команда

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

TestKey — название Key Container
С:\TestKey.xml — путь куда будет экспортирован файл
pri — добавить в экспорт приватный ключ

Импорт ключа

Команда

aspnet_regiis -pi "TestKey" С:\TestKey.xml

TestKey — название Key Container
С:\TestKey.xml — путь откуда будет экспортирован файл

Настройка прав

Чтобы ваше приложение или IIS могли работать с key container нужно настроить им права.

Делается это командой

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

TestKey — название Key Container
NT AUTHORITY\NETWORK SERVICE — кому будет выдан доступ до ключа

По умолчанию в IIS стоит ApplicationPoolIdentity для пула.

ссылка 2) ApplicationPoolIdentity описан как: В документации Microsoft (см.

  • This is also a least-privileged account. ApplicationPoolIdentity: When a new application pool is created, IIS creates a virtual account that has the name of the new application pool and that runs the application pool worker process under this account.

Поэтому чтобы IIS смог расшифровать конфиг, обязательно должна быть настроена Identity у пула на учетную запись или можно выбрать «NETWORK SERVICE» и для него дать права.

Добавление секции в 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>

Также key container и RsaProtectedConfigurationProvider определены глобально в файлах

NET\Framework\v4. C:\Windows\Microsoft. 30319\Config\machine.config, C:\Windows\Microsoft. 0. 0. NET\Framework64\v4. 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>

Шифрование

Само шифрование можно сделать тремя способами

Шифрование через командную строку

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

С:\Site — путь до файла с конфигом.

CustomRsaProtectedConfigurationProvider — наш провайдер указанный в конфиге с названием key container.

Шифрование через написанное приложение

private static string provider = "CustomRsaProtectedConfigurationProvider"; public static void ProtectConfiguration()

} 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(); }
}

Велосипед

Когда есть трансформация файлов и нужно зашифровать секции отдельно от всего конфига, то подойдет только самописный вариант. Мы создаем экземпляр класса RsaProtectedConfigurationProvider, берем узел из xml и шифруем его отдельно, затем заменяем в исходном xml узел на наш зашифрованный и сохраняем результат.

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); }
}

Ссылки

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть