外部应用程序配置和设置加密

External app config and encryption of settings

我们有许多应用程序和服务需要共享外部应用程序配置文件。外部文件包含一个 configSection,其中包含我们要加密的信息。 每个服务和应用程序都驻留在自己的应用程序文件夹中,这就是问题开始升级的地方。 在 App.config 中,可以使用“configSource”或“file”属性引用外部文件。无法使用“configSource”,因为外部配置文件不在应用程序文件夹或应用程序子文件夹中。因此,我们必须使用“文件”属性。

<customSettings file=”path to setting”/>

“customSettings”配置部分定义如下:

<configSections>
    <section name="customSettings" type="System.Configuration.NameValueFileSectionHandler, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</configSections>

然后我尝试使用如下代码加密 configSection:

Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection section = config.GetSection("customSettings");

if (!section.SectionInformation.IsProtected)
{
   section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
   config.Save();
}

因为我使用的是文件属性(我怀疑)。配置部分在 App.config 中加密,外部文件未加密。在 App.config 中加密的只是

<customSettings file=”path to setting”/>

。这没什么用。

这意味着个人应用程序和服务无法加密外部配置文件。 然后我有了一个想法,我会把一个小应用程序放在与外部配置文件相同的目录中。此应用程序的目的是通过使用“configSource”属性来加密外部配置文件。这种方法根本行不通。没有任何反应,也没有加密。

为了进一步调查,我将“customSettings”放在 App.config 中并成功加密了该部分。然后我将加密数据复制到外部文件以测试加密是否可以在外部配置文件中工作。这适用于“configSource”,但在使用“file”属性时会抛出异常。

抛出异常:

Unrecognized attribute 'configProtectionProvider'

因为我们必须在 app.config 中使用“文件”属性,所以我现在有 2 个问题。

  1. 无法加密外部文件。
  2. 如果外部文件是手动加密的,我无法使用“文件”属性读取它。

您可以采取的一个选项是创建自定义配置部分,避免使用 appsettings 部分并依赖自定义配置部分。您需要创建自己的处理程序来读取 course.You 的外部文件,此时可以指向任何文件,并且整个文件可能会被混淆并且不符合 XML 标准

或者您可以在应用程序设置中添加指向加密文件的设置并手动读入文件

经过大量研究并查看 NameValueFileSectionHandler 的代码后,我意识到 class 无法解析 file="file path" 属性指向的 configSection,如果外部加密的configSection。不知道这是否是 NameValueFileSectionHandler 中的错误。也许这里有人可以回答。

但是我最终编写了自己的 NameValueFileSectionHandler,它可以 return 一个 NameValueCollection 并处理加密的外部配置文件。

public class NameValueFileProtectedSectionHandler : IConfigurationSectionHandler
{
    public object Create(object parent, object configContext, XmlNode section)
    {
        object result = parent;

        XmlNode fileAttribute = section.Attributes.RemoveNamedItem("file");

        if (fileAttribute == null && fileAttribute.Value.Length == 0)
        {
            return new NameValueSectionHandler().Create(result, null, section);
        }

        IConfigErrorInfo configXmlNode = fileAttribute as IConfigErrorInfo;

        if (configXmlNode == null)
        {
            return null;
        }

        string directory = Path.GetDirectoryName(configXmlNode.Filename);
        string absoluteFilePath = Path.GetFullPath(directory + fileAttribute.Value);

        if (!File.Exists(absoluteFilePath))
        {
            throw new ConfigurationErrorsException(string.Format("external config file: {0} does not exists", absoluteFilePath));
        }

        var configXmlDocument = new ConfigXmlDocument();
        try
        {
            configXmlDocument.Load(absoluteFilePath);
        }
        catch (XmlException e)
        {
            throw new ConfigurationErrorsException(e.Message, e, absoluteFilePath, e.LineNumber);
        }

        if (section.Name != configXmlDocument.DocumentElement.Name)
        {
            throw new ConfigurationErrorsException(string.Format("Section name '{0}' in app.config does not match section name '{1}' in file '{2}'", section.Name, configXmlDocument.DocumentElement.Name, absoluteFilePath));
        }

        var nodeToDecrypt = configXmlDocument.DocumentElement["EncryptedData"];

        if (nodeToDecrypt == null)
        {
            throw new ConfigurationErrorsException(string.Format("External encrypted file {0} does not contain EncryptedData element", absoluteFilePath));
        }

        var protectionProvider = new DpapiProtectedConfigurationProvider();
        var decryptedConfigSection = protectionProvider.Decrypt(nodeToDecrypt);

        result = new NameValueSectionHandler().Create(result, null, decryptedConfigSection);

        return result;
    }
}

处理程序仅限于默认配置加密。但我可以想象,可以扩展 Create 函数以支持 app.config 文件中定义的自定义提供程序。