Зашифрованный файл XML при использовании IXmlSerializable (c#)

Как я могу написать XML-файл, который я сериализую с помощью IXmlSerializable в зашифрованном виде?

Я (де-) сериализую свои данные (структуру узлов, содержащих узлы, как папки файловой системы) в xml-файл:

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);
            }
        }
}

В приведенном выше коде показан код сериализации, десериализация опущена, потому что я не думаю, что это необходимо для решения проблемы.

Итак, как я могу записать файл в зашифрованном виде и расшифровать его перед десериализацией? Шифрование/дешифрование должно происходить в памяти (я не хочу сначала записывать незашифрованный файл и читать его, чтобы зашифровать)

изменить: под «шифрованием» я имею в виду, что файл не должен быть удобочитаемым или анализируемым другими программами, не зная, как его расшифровать (симметричный ключ)


person suriel    schedule 03.04.2020    source источник
comment
Как только вы выясните, что вы имеете в виду под шифрованием, вы сможете найти поток, который его реализует, и использовать код, аналогичный сжатию, например stackoverflow.com/questions/18212311/c-sharp-xml-compression. В текущем состоянии сообщение на самом деле не отвечает - пожалуйста, отредактируйте, чтобы уточнить, какое шифрование вы ищете (предпочтительно указывая на С# /.Net реализация).   -  person Alexei Levenkov    schedule 03.04.2020


Ответы (1)


ОБНОВЛЕНИЕ 1:

Вот та же реализация, но в виде двух методов, поддерживающих кодировку Unicode и, возможно, смягчающих проблемы с анализом кода.

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);
                }
            }
        }
    }
}

И использование следующее:

SerializeToEncryptedXmlFile(CharacterData.RandomInstance, "c:\\temp\\enc.xml");
CharacterData instance = DeserializeFromEncryptedXmlFile<CharacterData>("c:\\temp\\enc.xml");

ОРИГИНАЛЬНЫЙ ОТВЕТ:

Чтобы добиться полного шифрования, передайте экземпляр CryptoStream в XmlSerializer.

Вот пример использования AesManaged, охватывающий как шифрование, так и дешифрование.

Примечание. CharacterData — это некоторый XML-сериализуемый класс, который здесь не имеет значения.

// 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();
}
person Oguz Ozgul    schedule 03.04.2020
comment
Это работает как шарм, большое спасибо, НО: поскольку я использую FileStream вместо StreamWriter для сериализации данных, я больше не могу установить кодировку xml-файла, поэтому это не будет utf-16 анморе. Как это исправить? Кроме того, CodeAnalysis выдает мне предупреждения: CreateAesManaged не удаляется, в то время как зашифрованныйFileStream может быть удален более одного раза? - person suriel; 03.04.2020
comment
Я обновляю свой ответ сейчас. - person Oguz Ozgul; 03.04.2020
comment
Я попробовал ваше обновление, и, по крайней мере, проблема с uft-16 решена, большое спасибо. Я по-прежнему получаю предупреждения CodeAnalysis об объектах, удаленных несколько раз (encryptedFileStream и cryptoStream) и не удаленных объектах (объект AesManaged из CreateAesManaged). Я попробовал docs.microsoft.com/en- us/visualstudio/code-quality/ для решения первой проблемы, но я все еще получаю предупреждение. Я совершенно не уверен, может ли это быть ложным срабатыванием? - person suriel; 03.04.2020
comment
Многократное удаление связано с тем, что мы объявляем его с использованием и используем его в качестве базового потока для криптопотока, который может удалять наш базовый поток при удалении самого себя. - person Oguz Ozgul; 03.04.2020
comment
AES не распоряжается, потому что мы возвращаем его из метода, и нет никакой гарантии, что потребитель его распоряжается, я думаю. Просто объявите и инициализируйте его в строке, если это необходимо - person Oguz Ozgul; 03.04.2020
comment
ладно, хорошо. Я избавился от множественных предупреждений, применив уловку Microsoft, упомянутую в приведенной выше ссылке, и просто подавил предупреждение, касающееся AES-not-disposed. Мне кажется, он работает очень хорошо. Спасибо большое - person suriel; 04.04.2020