8 /// </summary>9 public class DataProtectionAPIProtectProvider : IProtectProvider10 {11 public IDataProtector DataProtector { get; }11 public IDataProtector DataProtector { get; }121314 /// <summary>15 /// The main constructor16 /// </summary>17 /// <param name="dataProtector">the <see cref="IDataProtector"/> interface obtained from Data Protection API</pa18 public DataProtectionAPIProtectProvider(IDataProtector dataProtector)18 public DataProtectionAPIProtectProvider(IDataProtector dataProtector)19 {20 DataProtector = dataProtector;21 }20 DataProtector = dataProtector;21 }2223 /// <summary>24 /// This methods create a new <see cref="IProtectProvider"/> for supporting per configuration value encryption s28 /// <returns>a derived <see cref="IProtectProvider"/> based on the <see cref="subkey"/> parameter</returns>29 public IProtectProvider CreateNewProviderFromSubkey(String key, String subkey)30 {31 return new DataProtectionAPIProtectProvider(DataProtector.CreateProtector(subkey));31 return new DataProtectionAPIProtectProvider(DataProtector.CreateProtector(subkey));32 }333440 /// <returns>the decrypted string</returns>41 public String Decrypt(String key, String encryptedValue)42 {43 return DataProtector.Unprotect(encryptedValue);43 return DataProtector.Unprotect(encryptedValue);44 }454652 /// <returns>the encrypted string</returns>53 public String Encrypt(String key, String plainTextValue)54 {55 return DataProtector.Protect(plainTextValue);55 return DataProtector.Protect(plainTextValue);56 }57 }58}104 foreach (var protectFileOption in ProtectFilesOptions)105 if (protectFileOption.FilenameRegex.Match(filename).Success)106 {107 replacedContent = protectFileOption.ProtectFileProcessor.ProtectFile(fileContent, protectProviderCon107 replacedContent = protectFileOption.ProtectFileProcessor.ProtectFile(fileContent, protectProviderCon108 break;109 }110147 /// <exception cref="ArgumentException"></exception>148 public static String ProtectConfigurationValue(this IProtectProviderConfigurationData protectProviderConfigurati149 {150 return ProtectConfigurationValueInternal(protectProviderConfigurationData, key, value);150 return ProtectConfigurationValueInternal(protectProviderConfigurationData, key, value);151 }152153160 /// <returns></returns>161 private static String ProtectConfigurationValueInternal(IProtectProviderConfigurationData protectProviderConfigu162 {163 if (value == null)164 return null;163 if (value == null)164 return null;165166 protectProviderConfigurationData.CheckConfigurationIsValid();166 protectProviderConfigurationData.CheckConfigurationIsValid();167168 return protectProviderConfigurationData.ProtectRegex.Replace(value, (me) =>169 {170 var subPurposePresent = !String.IsNullOrEmpty(me.Groups["subPurpose"]?.Value);171172 var protectProvider = protectProviderConfigurationData.ProtectProvider;173174 if (subPurposePresent)175 protectProvider = protectProviderConfigurationData.ProtectProvider.CreateNewProviderFromSubkey(key, 176177 var encryptedValue = protectProvider.Encrypt(key, me.Groups["protectData"].Value);178179 // if the encryption function returns null or empty store the original value unencrypted180 if (String.IsNullOrEmpty(encryptedValue))181 return me.Groups["protectData"].Value;182 else183 return protectProviderConfigurationData.ProtectedReplaceString.Replace("${subPurposePattern}", subPu184 });168 return protectProviderConfigurationData.ProtectRegex.Replace(value, (me) =>169 {170 var subPurposePresent = !String.IsNullOrEmpty(me.Groups["subPurpose"]?.Value);171172 var protectProvider = protectProviderConfigurationData.ProtectProvider;173174 if (subPurposePresent)175 protectProvider = protectProviderConfigurationData.ProtectProvider.CreateNewProviderFromSubkey(key, 176177 var encryptedValue = protectProvider.Encrypt(key, me.Groups["protectData"].Value);178179 // if the encryption function returns null or empty store the original value unencrypted180 if (String.IsNullOrEmpty(encryptedValue))181 return me.Groups["protectData"].Value;182 else183 return protectProviderConfigurationData.ProtectedReplaceString.Replace("${subPurposePattern}", subPu184 });185 }186187235 {236 var environmentVariables = Environment.GetEnvironmentVariables(environmentTarget);237238 foreach (String key in environmentVariables.Keys)239 if (String.IsNullOrEmpty(prefix) || key.StartsWith(prefix))240 Environment.SetEnvironmentVariable(key, protectProviderConfigurationData.ProtectConfigurationValue(k238 foreach (String key in environmentVariables.Keys)239 if (String.IsNullOrEmpty(prefix) || key.StartsWith(prefix))240 Environment.SetEnvironmentVariable(key, protectProviderConfigurationData.ProtectConfigurationValue(k241 }24224350 /// <summary>51 /// The actual provider performing the encryption/decryption, <see cref="IProtectProvider"/> interface52 /// </summary>53 public IProtectProvider ProtectProvider { get; protected set; }53 public IProtectProvider ProtectProvider { get; protected set; }5455 /// <summary>56 /// a regular expression which captures the data to be decrypted in a named group called protectData57 /// </summary>58 public Regex ProtectedRegex { get; protected set; }58 public Regex ProtectedRegex { get; protected set; }596061 /// <summary>62 /// a regular expression which captures the data to be encrypted in a named group called protectData63 /// </summary>64 public Regex ProtectRegex { get; protected set; }64 public Regex ProtectRegex { get; protected set; }656667 /// <summary>68 /// a string replacement expression which captures the substitution which must be applied for transforming unenc69 /// </summary>70 public String ProtectedReplaceString { get; protected set; }70 public String ProtectedReplaceString { get; protected set; }717273 /// <summary>75 /// </summary>76 public virtual void CheckConfigurationIsValid()77 {78 ProtectRegex = ProtectRegex ?? new Regex(DefaultProtectRegexString);79 if (!ProtectRegex.GetGroupNames().Contains("protectData"))78 ProtectRegex = ProtectRegex ?? new Regex(DefaultProtectRegexString);79 if (!ProtectRegex.GetGroupNames().Contains("protectData"))80 throw new ArgumentException("ProtectRegex must contain a group named protectedData!", nameof(ProtectRege8182 ProtectedRegex = ProtectedRegex ?? new Regex(DefaultProtectedRegexString);83 if (!ProtectedRegex.GetGroupNames().Contains("protectedData"))82 ProtectedRegex = ProtectedRegex ?? new Regex(DefaultProtectedRegexString);83 if (!ProtectedRegex.GetGroupNames().Contains("protectedData"))84 throw new ArgumentException("ProtectedRegex must contain a group named protectedData!", nameof(Protected8586 ProtectedReplaceString = !String.IsNullOrEmpty(ProtectedReplaceString) ? ProtectedReplaceString : DefaultPro87 if (!ProtectedReplaceString.Contains("${protectedData}"))86 ProtectedReplaceString = !String.IsNullOrEmpty(ProtectedReplaceString) ? ProtectedReplaceString : DefaultPro87 if (!ProtectedReplaceString.Contains("${protectedData}"))88 throw new ArgumentException("ProtectedReplaceString must contain ${protectedData}!", nameof(ProtectedRep8990 if (ProtectProvider == null)90 if (ProtectProvider == null)91 throw new ArgumentException("ProtectProvider must not be null!", nameof(ProtectProvider));92 }92 }939495 /// <summary>187 public class JsonWithCommentsProtectFileProcessor : IProtectFileProcessor188 {189 protected JsonSerializerOptions JsonSerializerOptions { get; set; }190 protected JsonReaderOptions JsonReaderOptions { get; set; }191 protected JsonWriterOptions JsonWriterOptions { get; set; }190 protected JsonReaderOptions JsonReaderOptions { get; set; }191 protected JsonWriterOptions JsonWriterOptions { get; set; }192193194 /// <summary>217 {218 return protectRegex.Replace(rawFileText, me =>219 {220 var utf8JsonReader = new Utf8JsonReader(Encoding.UTF8.GetBytes($"\"{me.Value}\"").AsSpan(), JsonReaderOp221 var reencodedJsonMemoryStream = new MemoryStream();222 var utf8JsonWriter = new Utf8JsonWriter(reencodedJsonMemoryStream, JsonWriterOptions);220 var utf8JsonReader = new Utf8JsonReader(Encoding.UTF8.GetBytes($"\"{me.Value}\"").AsSpan(), JsonReaderOp221 var reencodedJsonMemoryStream = new MemoryStream();222 var utf8JsonWriter = new Utf8JsonWriter(reencodedJsonMemoryStream, JsonWriterOptions);223224 if (utf8JsonReader.Read())224 if (utf8JsonReader.Read())225 {226 utf8JsonWriter.WriteStringValue(protectFunction(String.Empty, utf8JsonReader.GetString()));227 utf8JsonWriter.Flush();228 return Encoding.UTF8.GetString(reencodedJsonMemoryStream.ToArray()).Replace("\"",String.Empty);226 utf8JsonWriter.WriteStringValue(protectFunction(String.Empty, utf8JsonReader.GetString()));227 utf8JsonWriter.Flush();228 return Encoding.UTF8.GetString(reencodedJsonMemoryStream.ToArray()).Replace("\"",String.Empty);229 }230 else231 throw new JsonException($"Found invalid JSON value: {me.Value}!");23 /// <returns>a derived <see cref="IProtectProvider"/> based on the <see cref="subkey"/> parameter</returns>24 public IProtectProvider CreateNewProviderFromSubkey(String key, String subkey)25 {26 return this;26 return this;27 }282935 /// <returns>the decrypted string</returns>36 public String Decrypt(String key, String encryptedValue)37 {38 return encryptedValue;38 return encryptedValue;39 }404147 /// <returns>the encrypted string</returns>48 public String Encrypt(String key, String plainTextValue)49 {50 return plainTextValue;50 return plainTextValue;51 }52 }53}16 [DebuggerDisplay("Provider = Protected{Provider}")]17 public class ProtectedConfigurationProvider : IConfigurationProvider, IDisposable18 {19 protected IConfigurationProvider Provider { get; }20 protected IProtectProviderConfigurationData ProtectProviderConfigurationData { get; }19 protected IConfigurationProvider Provider { get; }20 protected IProtectProviderConfigurationData ProtectProviderConfigurationData { get; }2122 protected ConfigurationReloadToken ReloadToken;23133 // the speed improvement is more than 3000 times!134 if (((dataProperty = ProviderData) != null))135 {136 foreach (var key in dataProperty.Keys.ToList())136 foreach (var key in dataProperty.Keys.ToList())137 {138 if (!String.IsNullOrEmpty(dataProperty[key]))139 Provider.Set(key, ProtectProviderConfigurationData.ProtectedRegex.Replace(dataProperty[key], me 140 {141142 var subPurposePresent = !String.IsNullOrEmpty(me.Groups["subPurpose"]?.Value);143144 IProtectProvider protectProvider = ProtectProviderConfigurationData.ProtectProvider;145146 if (subPurposePresent)147 protectProvider = protectProvider.CreateNewProviderFromSubkey(key, me.Groups["subPurpose148149 return protectProvider.Decrypt(key, me.Groups["protectedData"].Value);150 }));138 if (!String.IsNullOrEmpty(dataProperty[key]))139 Provider.Set(key, ProtectProviderConfigurationData.ProtectedRegex.Replace(dataProperty[key], me 140 {141142 var subPurposePresent = !String.IsNullOrEmpty(me.Groups["subPurpose"]?.Value);143144 IProtectProvider protectProvider = ProtectProviderConfigurationData.ProtectProvider;145146 if (subPurposePresent)147 protectProvider = protectProvider.CreateNewProviderFromSubkey(key, me.Groups["subPurpose148149 return protectProvider.Decrypt(key, me.Groups["protectedData"].Value);150 }));151 }152 }153 else297 String value;298299 // protects all element attribute values300 foreach (var attribute in element.Attributes())300 foreach (var attribute in element.Attributes())301 {302 value = attribute.Value;303 if (protectRegex.IsMatch(value))304 attribute.Value = protectFunction(attribute.Name.LocalName, value);302 value = attribute.Value;303 if (protectRegex.IsMatch(value))304 attribute.Value = protectFunction(attribute.Name.LocalName, value);305 }306307 if (element.HasElements)307 if (element.HasElements)308 {309 // recursively protects nested elements310 foreach (var nestedElement in element.Elements())311 ProtectXmlNodes(nestedElement, protectRegex, protectFunction);310 foreach (var nestedElement in element.Elements())311 ProtectXmlNodes(nestedElement, protectRegex, protectFunction);312 }313 else314 {315 // protects element value if it has no children elements316 value = element.Value;317 if (protectRegex.IsMatch(value))318 element.Value = protectFunction(element.Name.LocalName, value);316 value = element.Value;317 if (protectRegex.IsMatch(value))318 element.Value = protectFunction(element.Name.LocalName, value);319 }320 }320 }321 }322}