【问题标题】:Encrypted XML file when using IXmlSerializable (c#)使用 IXmlSerializable (c#) 时加密的 XML 文件
【发布时间】:2020-04-03 07:12:48
【问题描述】:

如何编写一个 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);
            }
        }
}

上面的代码显示了序列化代码,省略了反序列化,因为我认为不需要它来解决问题。

那么如何在反序列化之前编写加密和解密文件?加密/解密应该发生在内存中(我不想先写一个未加密的文件然后读回加密它)

编辑:“加密”是指在不知道如何解密的情况下,文件不应该是人类可读或可被其他程序解析的(对称密钥)

【问题讨论】:

标签: c# encryption serialization xmlserializer ixmlserializable


【解决方案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();
}

【讨论】:

  • 这就像一个魅力,非常感谢你,但是:因为我使用 FileStream 而不是 StreamWriter 来序列化数据,我不能再设置 xml 文件的编码,所以它不会是 utf-16 了。如何解决?另外 CodeAnalysis 给了我警告: CreateAesManaged 没有被释放,而 encryptedFileStream 可以被多次释放?
  • 我现在正在更新我的答案。
  • 我试过你的更新,至少 uft-16-problem 已经解决了,非常感谢。我仍然收到有关多次处理的对象(encryptedFileStream 和 cryptoStream)和未处理的对象(CreateAesManaged 的​​ AesManaged 对象)的 CodeAnalysis 警告。我尝试docs.microsoft.com/en-us/visualstudio/code-quality/… 解决第一个问题,但我仍然收到警告。我很不确定这是否可能是误报?
  • 多次处理是因为我们都使用 using 声明它并将其用作加密流的基本流,这可能会在自行处理时处理我们的底层流。
  • AES not disposes 是因为我们从一个方法中返回它并且不能保证它的使用者会处理它,我认为。如果需要,只需在线声明和初始化它
猜你喜欢
  • 1970-01-01
  • 2010-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-15
  • 1970-01-01
相关资源
最近更新 更多