ErocM
ErocM

Reputation: 4662

Encrypting properties before saving class to xml file

I am attempting to save a few pieces of the connection string as encrypted text to an XML from my class.

Here is my class:

using System.Data.SqlClient;
using TechGuyComputing.CompleteOrganizerWPF.Data;

namespace TechGuyComputing.CompleteOrganizerWPF.MiscClasses
{
  public class AppSetting
  {
    private string _dataSource;
    private string _intitialCatalog;
    private string _userId;
    private string _password;

    public string DataSource
    {
      set => _dataSource = Encryption.SimpleEncryptWithPassword(value, GlobalConstants.EncryptionPassword);
      get => Encryption.SimpleDecryptWithPassword(_dataSource, GlobalConstants.EncryptionPassword);
    }

    public string IntitialCatalog
    {
      set => _intitialCatalog = Encryption.SimpleEncryptWithPassword(value, GlobalConstants.EncryptionPassword);
      get => Encryption.SimpleDecryptWithPassword(_intitialCatalog, GlobalConstants.EncryptionPassword);
    }

    public string UserId
    {
      set => _userId = Encryption.SimpleEncryptWithPassword(value, GlobalConstants.EncryptionPassword);
      get => Encryption.SimpleDecryptWithPassword(_userId, GlobalConstants.EncryptionPassword);
    }

    public string Password
    {
      set => _password = Encryption.SimpleEncryptWithPassword(value, GlobalConstants.EncryptionPassword);
      get => Encryption.SimpleDecryptWithPassword(_password, GlobalConstants.EncryptionPassword);
    }

    public bool IntegratedSecurity { set; get; }
    public bool MultipleActiveResultSets { set; get; }
    public bool PersistSecurityInfo { set; get; }
  }

  internal static class AppSettings
  {
    public static AppSetting ApplicationSettings;

    public static SqlConnection ConnectionString { get; private set; }


    static AppSettings()
    {
      if (ApplicationSettings == null)
      {
        ApplicationSettings = XmlReader.GetAppSettingsFromXmlFile();
        SetConnectionString();
      }
    }

    public static void SaveAppSettings()
    {
      if (ApplicationSettings == null)
      {
        ApplicationSettings = new AppSetting();
      }
      XmlReader.WriteAppSettingsToXmlFile(ApplicationSettings);
      SetConnectionString();
    }

    private static void SetConnectionString()
    {
      if (string.IsNullOrEmpty(ApplicationSettings.DataSource) || string.IsNullOrEmpty(ApplicationSettings.IntitialCatalog))
      {
        ConnectionString = new SqlConnection();
        return;
      }

      var builder = new SqlConnectionStringBuilder
      {
        DataSource = ApplicationSettings.DataSource,
        InitialCatalog = ApplicationSettings.IntitialCatalog,
        IntegratedSecurity = ApplicationSettings.IntegratedSecurity,
        MultipleActiveResultSets = ApplicationSettings.MultipleActiveResultSets,
        PersistSecurityInfo = ApplicationSettings.PersistSecurityInfo,
        UserID = ApplicationSettings.UserId,
        Password = ApplicationSettings.Password
      };

      ConnectionString = new SqlConnection(builder.ConnectionString);
    }

  }
}

And this is how I am saving the XML file:

using System.IO;
using System.Xml.Serialization;

namespace TechGuyComputing.CompleteOrganizerWPF.MiscClasses
{
  internal static class XmlReader
  {

    public static void WriteAppSettingsToXmlFile(AppSetting appSetting)
    {
      var xs = new XmlSerializer(typeof(AppSetting));
      var tw = new StreamWriter(GlobalConstants.XmlFile);
      xs.Serialize(tw, appSetting);
    }

    public static AppSetting GetAppSettingsFromXmlFile()
    {
      if (!File.Exists(GlobalConstants.XmlFile))
      {
        return new AppSetting();
      }

      using (var sr = new StreamReader(GlobalConstants.XmlFile))
      {
        XmlSerializer xs = new XmlSerializer(typeof(AppSetting));
        return (AppSetting)xs.Deserialize(sr);
      }
    }


  }
}

My save is working perfectly but it is not saving the values as encrypted strings.

I thought this would handle it on the fly but it's not doing anything:

public string DataSource
{
  set => _dataSource = Encryption.SimpleEncryptWithPassword(value, GlobalConstants.EncryptionPassword);
  get => Encryption.SimpleDecryptWithPassword(_dataSource, GlobalConstants.EncryptionPassword);
}

I am not getting any error messages, it's just not encrypting the data.

Any suggestions how I encrypt certain properties before they are saved?

EDIT: I'd prefer not to encrypt the entire file if I can prevent it. I would like to only encrypt the properties that I choose.

Upvotes: 2

Views: 706

Answers (1)

dbc
dbc

Reputation: 116516

Your problem is that XmlSerializer serializes only public properties and fields -- and the public properties in your AppSetting class are all unencrypted. From the docs

XML serialization serializes only the public fields and property values of an object into an XML stream. ...

XML serialization does not convert methods, indexers, private fields, or read-only properties (except read-only collections). To serialize all an object's fields and properties, both public and private, use the DataContractSerializer instead of XML serialization.

Thus your options are:

  1. Make public properties for the encrypted members and mark the plaintext properties with XmlIgnore like so:

    [System.ComponentModel.Browsable(false), System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
    [XmlElement("DataSource")] // Optionally change the element name to be <DataSource>
    public string EncryptedDataSource { get; set; }
    
    [XmlIgnore]
    public string DataSource
    {
        set => EncryptedDataSource = Encryption.SimpleEncryptWithPassword(value, GlobalConstants.EncryptionPassword);
        get => Encryption.SimpleDecryptWithPassword(EncryptedDataSource, GlobalConstants.EncryptionPassword);
    }
    

    Demo fiddle #1 here.

  2. Switch to DataContractSerializer. First modify your class as follows:

    [DataContract]
    public class AppSetting
    {
        [DataMember(Name = "DataSource")]
        private string _dataSource;
        [DataMember(Name = "IntitialCatalog")]
        private string _intitialCatalog;
        [DataMember(Name = "UserId")]
        private string _userId;
        [DataMember(Name = "Password")]
        private string _password;
    
        // Remainder unchanged
    

    Then modify XmlReader as follows:

    public static void WriteAppSettingsToXmlFile(AppSetting appSetting)
    {
        var serializer = new DataContractSerializer(typeof(AppSetting));
        using (var stream = new FileStream(GlobalConstants.XmlFile, FileMode.Create))
        {
            serializer.WriteObject(stream, appSetting);
        }
    }
    
    public static AppSetting GetAppSettingsFromXmlFile()
    {
        if (!File.Exists(GlobalConstants.XmlFile))
        {
            return new AppSetting();
        }
        using (var stream = File.OpenRead(GlobalConstants.XmlFile))
        {
            var serializer = new DataContractSerializer(typeof(AppSetting));
            return (AppSetting)serializer.ReadObject(stream);
        }
    }
    

    The resulting properties will all be encrypted.

    Demo fiddle #2 here.

Notes:

  • In WriteAppSettingsToXmlFile() you do not dispose the StreamWriter. This will leave the file open and possibly lead to errors later. Instead, do:

    public static void WriteAppSettingsToXmlFile(AppSetting appSetting)
    {
        var xs = new XmlSerializer(typeof(AppSetting));
        using (var tw = new StreamWriter(GlobalConstants.XmlFile))
        {
            xs.Serialize(tw, appSetting);
        }
    }
    
  • While properties serialized with XmlSerializer must be public, you can hide them a little by marking them with [Browsable(false)], [EditorBrowsable(EditorBrowsableState.Never)] and [DebuggerBrowsable(DebuggerBrowsableState.Never)],

Upvotes: 2

Related Questions