How to Encrypt Selected Properties When Serializing My Objects

How can I encrypt selected properties when serializing my objects?

My solution:

    public string PasswordEncrypted { get; set; }

[JsonIgnore]
public string Password
{
get
{
var encrypted = Convert.FromBase64String(PasswordEncrypted);
var data = ProtectedData.Unprotect(encrypted, AdditionalEntropy, DataProtectionScope.LocalMachine);
var res = Encoding.UTF8.GetString(data);
return res;
}
set
{
var data = Encoding.UTF8.GetBytes(value);
var encrypted = ProtectedData.Protect(data, AdditionalEntropy, DataProtectionScope.LocalMachine);
PasswordEncrypted = Convert.ToBase64String(encrypted);
}

(can be made less verbose)

serialize class object that contain secret data - serialization with encryption

After realizing what you needed the following methods decorated with the correct attributes should work... you just need some custom code to run during and after serialization and then after deserialization...

// Save your password so you can reset it after the object has been serialized.
[NonSerialized()]
private string SavedPassword;

// This saves the value of Password and Encrpts it so it will be stored Encrypted.
// I am leaving out the Encrypt code to make it cleaner here.
[OnSerializing()]
internal void OnSerializingMethod(StreamingContext context)
{
SavedPassword = Password;
Password = Encrypt(Password);
}

// reset the Password after serialization so you can continue to use your object.
[OnSerialized()]
internal void OnSerializedMethod(StreamingContext context)
{
Password = SavedPassword;
}

// On deserialize you need to Decrypt your Password.
[OnDeserialized()]
internal void OnDeserializedMethod(StreamingContext context)
{
Password = Decrypt(Password);
}

Explanation of the attributes and methods...

[NonSerialized()] - tells the serializer to not include this field / property in the serialized object.

[OnSerializing()] - tells the serializer to call this method before serializing the object. Our encryption code goes here because we want the encrypted value of the password to be serialized.

[OnSerialized()] - tells the serializer to call this method after the object has been serialized. We need to reset the password to it's unencrypted state here. (instead of saving the password unencrypted you could do a decrypt here just as easily)

[OnDeserialized()] - tells the serializer to call this method after the object has been deserialized. This is where our decrypt goes because the object isn't ready to be used until we decrypt the password.

With these attributes and methods in place the Password property will automatically be encrypted during serialization and decrypted during deserialization.

Encrypting properties before saving class to xml file

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)],



Related Topics



Leave a reply



Submit