背景知识
我的任务是设置CI。 决定使用配置文件的转换并以加密形式存储机密数据。 您可以使用密钥容器对它们进行加密和解密。
钥匙箱
每个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-我们的密钥容器的名称
密钥导出
团队
aspnet_regiis -px "TestKey" :\TestKey.xml -pri
TestKey-名称密钥容器
C:\ TestKey.xml-文件将导出到的路径
pri-添加私钥以导出
导入密钥
团队
aspnet_regiis -pi "TestKey" :\TestKey.xml
TestKey-名称密钥容器
C:\ TestKey.xml-从中导出文件的路径
设定权
为了使您的应用程序或IIS使用密钥容器,您需要为其配置权限。
这是由团队完成的
aspnet_regiis -pa "TestKey" "NT AUTHORITY\NETWORK SERVICE"
TestKey-名称密钥容器
NT AUTHORITY \ NETWORK SERVICE-将为谁提供访问密钥的权限
默认情况下,IIS具有池的ApplicationPoolIdentity。
Microsoft文档(请参阅链接2)将ApplicationPoolIdentity描述为:
ApplicationPoolIdentity :创建新的应用程序池时,IIS将创建一个虚拟帐户,该帐户具有新应用程序池的名称,并在该帐户下运行应用程序池工作进程。 这也是特权最少的帐户。
因此,为了使IIS能够解密配置,必须在帐户的池中配置身份,或者您可以选择“网络服务”并为其授予权限。
在配置中添加一个部分
<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>
此外,密钥容器和RsaProtectedConfigurationProvider在文件中全局定义
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>
= “System.Configuration.DpapiProtectedConfigurationProvider,System.Configuration,版本= 4.0.0.0,文化=中性公钥= b03f5f7f11d50a3a”描述= “CryptProtectData和CryptUnProtectData的Windows API用于加密和解密” useMachineProtection = <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"
C:\ Site-带有配置文件的路径。
CustomRsaProtectedConfigurationProvider-在配置中指定的提供程序,称为密钥容器。
通过书面申请进行加密
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(); } }
单车
当进行文件转换时,您需要将部分与整个配置分开加密,那么仅适用于自写版本。 我们创建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/zh-CN/previous-versions/53tyfkaw2.support.microsoft.com/zh-CN/help/4466942/understanding-identities-in-iis