How can I write a XML-File to which I serialize using IXmlSerializable in an encrypted way?
I (de-)serialize my data (a structure of nodes containig nodes, just like filesystemfolders) into a xml-File:
public class DataNodeCollection : List<DataNode>, IXmlSerializable
{
internal void Serialize()
{
string sFilename = getFilename();
using (var writer = new StreamWriter(sFilename, false, Encoding.Unicode))
{
var serializer = new XmlSerializer(this.GetType(), new XmlRootAttribute("SystemNodes"));
serializer.Serialize(writer, this);
writer.Flush();
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("FileFormatVersion", CurrentFormatVersion.ToString(CultureInfo.InvariantCulture));
foreach (DataNode elem in this)
{
var innerSerializer = new XmlSerializer(typeof(DataNode), new XmlRootAttribute(elem.Name));
innerSerializer.Serialize(writer, elem);
}
}
}
public class DataNode : IXmlSerializable
{
private IDictionary<string, string> _mapAttributes = new Dictionary<string, string>();
private IList<DataNode> _subNodes = new List<DataNode>();
public string Name { get; protected set; }
public void WriteXmlXmlWriter writer)
{
foreach (string sKey in _mapAttributes.Keys)
{
writer.WriteAttributeString(sKey, _mapAttributes[sKey]);
}
foreach (DataNode node in _subNodes)
{
var innerSerializer = new XmlSerializer(typeof(DataNode), new XmlRootAttribute(node.Name));
innerSerializer.Serialize(writer, node);
}
}
}
The code above shows the serialilzation-code, deserialisation is omitted because I don't think its needed to get the problem.
So how can I write the file encrypted and decrypt it before deserialising? The encryption/decryption should happen in memory (I don't want to write an unencrypted file first and read it back to encrypt it)
edit: With "encryption" I mean the file should not be human readable or parseable by other programs without knowing how to decrypt it (symmetric key)
Here is the same implementation but as two methods supporting Unicode encoding and probably mitigating the code analysis issues.
static void SerializeToEncryptedXmlFile(object graph, string filePath)
{
using (FileStream encryptedFileStream = File.Create(filePath))
{
using (AesManaged aesManaged = CreateAesManaged())
{
using
(
CryptoStream cryptoStream = new CryptoStream
(
encryptedFileStream, CreateAesManaged().CreateEncryptor(), CryptoStreamMode.Write
)
)
{
using (StreamWriter unicodeStreamWriter = new StreamWriter(cryptoStream, Encoding.Unicode))
{
{
new XmlSerializer(typeof(CharacterData)).Serialize(unicodeStreamWriter, CharacterData.RandomInstance);
// If you dont use a using statement for the cryptoStream,
// Don't forget to call FlushFinalBlock yourself
// Or you will have padding problems.
// cryptoStream.FlushFinalBlock();
}
}
}
}
}
}
public static TResult DeserializeFromEncryptedXmlFile<TResult>(string filePath)
{
using (FileStream encryptedFileStream = File.OpenRead(filePath))
{
using (AesManaged aesManaged = CreateAesManaged())
{
using
(
CryptoStream cryptoStream = new CryptoStream
(
encryptedFileStream, aesManaged.CreateDecryptor(), CryptoStreamMode.Read
)
)
{
using (StreamReader unicodeStreamReader = new StreamReader(cryptoStream))
{
return (TResult)new XmlSerializer(typeof(CharacterData)).Deserialize(unicodeStreamReader);
}
}
}
}
}
And the usage is as follows:
SerializeToEncryptedXmlFile(CharacterData.RandomInstance, "c:\\temp\\enc.xml");
CharacterData instance = DeserializeFromEncryptedXmlFile<CharacterData>("c:\\temp\\enc.xml");
To achieve complete encryption, pass a CryptoStream
instance to the XmlSerializer.
Here is a sample using AesManaged covering both encryption and decryption.
Note: CharacterData is some XML serializable class which is not relevant here.
// Returns AesManaged with 256 bit key, 128 bit IV, PKCS7 padding and using CBC mode
private static AesManaged CreateAesManaged()
{
return new AesManaged()
{
Key = Encoding.ASCII.GetBytes("This is the key%This is the key%"),
IV = Encoding.ASCII.GetBytes("This is the IV%%")
};
}
static void Main(string[] args)
{
// Serialization / Encryption:
using (FileStream encryptedFileStream = File.Create("C:\\temp\\enc.xml"))
{
using
(
CryptoStream cryptoStream = new CryptoStream
(
encryptedFileStream, CreateAesManaged().CreateEncryptor(), CryptoStreamMode.Write
)
)
{
new XmlSerializer(typeof(CharacterData)).Serialize(cryptoStream, CharacterData.RandomInstance);
// If you dont use a using statement for the cryptoStream,
// Don't forget to call FlushFinalBlock yourself
// Or you will have padding problems.
// cryptoStream.FlushFinalBlock();
}
}
// De-Serialization / Decryption:
using (FileStream encryptedFileStream = File.OpenRead("C:\\temp\\enc.xml"))
{
using
(
CryptoStream cryptoStream = new CryptoStream
(
encryptedFileStream, CreateAesManaged().CreateDecryptor(), CryptoStreamMode.Read
)
)
{
CharacterData instance = (CharacterData)new XmlSerializer(typeof(CharacterData)).Deserialize(cryptoStream);
}
}
Console.ReadLine();
}