File: System\Configuration\DpapiProtectedConfigurationProvider.cs
Web Access
Project: src\src\libraries\System.Configuration.ConfigurationManager\src\System.Configuration.ConfigurationManager.csproj (System.Configuration.ConfigurationManager)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Specialized;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
 
namespace System.Configuration
{
    [SupportedOSPlatform("windows")]
    public sealed class DpapiProtectedConfigurationProvider : ProtectedConfigurationProvider
    {
        private bool _useMachineProtection = true;
        private string _keyEntropy;
 
        public override XmlNode Decrypt(XmlNode encryptedNode)
        {
            if (encryptedNode.NodeType != XmlNodeType.Element ||
                encryptedNode.Name != "EncryptedData")
            {
                throw new ConfigurationErrorsException(SR.DPAPI_bad_data);
            }
 
            XmlNode cipherNode = TraverseToChild(encryptedNode, "CipherData", false);
            if (cipherNode == null)
                throw new ConfigurationErrorsException(SR.DPAPI_bad_data);
 
            XmlNode cipherValue = TraverseToChild(cipherNode, "CipherValue", true);
            if (cipherValue == null)
                throw new ConfigurationErrorsException(SR.DPAPI_bad_data);
 
            string encText = cipherValue.InnerText;
            if (encText == null)
                throw new ConfigurationErrorsException(SR.DPAPI_bad_data);
 
            string decText = DecryptText(encText);
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.PreserveWhitespace = true;
            xmlDocument.LoadXml(decText);
            return xmlDocument.DocumentElement;
        }
 
        public override XmlNode Encrypt(XmlNode node)
        {
            string text = node.OuterXml;
            string encText = EncryptText(text);
            string pre = @"<EncryptedData><CipherData><CipherValue>";
            string post = @"</CipherValue></CipherData></EncryptedData>";
            string xmlText = pre + encText + post;
 
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.PreserveWhitespace = true;
            xmlDocument.LoadXml(xmlText);
            return xmlDocument.DocumentElement;
        }
 
        private string EncryptText(string clearText)
        {
            if (clearText == null || clearText.Length < 1)
                return clearText;
 
            byte[] inputData = PrepareDataBlob(clearText);
            byte[] entropyData = PrepareDataBlob(_keyEntropy);
 
            byte[] encryptedData = ProtectedData.Protect(
                userData: inputData,
                optionalEntropy: entropyData,
                scope: UseMachineProtection ? DataProtectionScope.LocalMachine : DataProtectionScope.CurrentUser);
 
            return Convert.ToBase64String(encryptedData);
        }
 
        private string DecryptText(string encText)
        {
            if (encText == null || encText.Length < 1)
                return encText;
 
            byte[] inputData = Convert.FromBase64String(encText);
            byte[] entropyData = PrepareDataBlob(_keyEntropy);
 
            byte[] decryptedData = ProtectedData.Unprotect(
                encryptedData: inputData,
                optionalEntropy: entropyData,
                scope: UseMachineProtection ? DataProtectionScope.LocalMachine : DataProtectionScope.CurrentUser);
 
            return Encoding.Unicode.GetString(decryptedData);
        }
 
        public bool UseMachineProtection { get { return _useMachineProtection; } }
 
        public override void Initialize(string name, NameValueCollection configurationValues)
        {
            base.Initialize(name, configurationValues);
            _useMachineProtection = GetBooleanValue(configurationValues, "useMachineProtection", true);
            _keyEntropy = configurationValues["keyEntropy"];
            configurationValues.Remove("keyEntropy");
            if (configurationValues.Count > 0)
                throw new ConfigurationErrorsException(SR.Format(SR.Unrecognized_initialization_value, configurationValues.GetKey(0)));
        }
 
        private static XmlNode TraverseToChild(XmlNode node, string name, bool onlyChild)
        {
            foreach (XmlNode child in node.ChildNodes)
            {
                if (child.NodeType != XmlNodeType.Element)
                    continue;
                if (child.Name == name)
                    return child; // found it!
                if (onlyChild)
                    return null;
            }
 
            return null;
        }
 
        private static byte[] PrepareDataBlob(string s)
        {
            return (s != null) ? Encoding.Unicode.GetBytes(s) : Array.Empty<byte>();
        }
 
        private static bool GetBooleanValue(NameValueCollection configurationValues, string valueName, bool defaultValue)
        {
            string s = configurationValues[valueName];
            if (s == null)
                return defaultValue;
            configurationValues.Remove(valueName);
            if (s == "true")
                return true;
            if (s == "false")
                return false;
            throw new ConfigurationErrorsException(SR.Format(SR.Config_invalid_boolean_attribute, valueName));
        }
    }
}