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:
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.
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 unchangedThen 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 theStreamWriter
. 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
How to Pass Constructor Parameters to Unity's Resolve() Method
Why New Fb API 2.4 Returns Null Email on MVC 5 with Identity and Oauth 2
What's the Difference Between System.Type and System.Runtimetype in C#
How to Set Up a Web API Controller for Multipart/Form-Data
How Does MVC 4 List Model Binding Work
How to Access Winform Textbox Control from Another Class
How to Indefinitely Pause a Thread
Using Reflection in C# to Get Properties of a Nested Object
Using Image Control in Wpf to Display System.Drawing.Bitmap
How to Implement a Progress Bar Using the Mvvm Pattern
How to Post Using Httpclient Content Type = Application/X-Www-Form-Urlencoded
Best .Net Memory and Performance Profiler
How Does Hashset Compare Elements for Equality
Can't Connect to Localhost on SQL Server Express 2012/2016
Implicit VS Explicit Interface Implementation
How to Use Assert to Verify That an Exception Has Been Thrown