如何在加密和解密期间避免额外的内存使用?

Sim*_*mon 9 .net c# encryption

所以我有一个基本的加密类.请注意,这是一个简化的实现来说明问题.

现在我想这两个方法都有一个额外的字节数组和字符串实例.

xmlStringbytesEncrypt

decryptedStringdecryptedBytesDecrypt

那么如何在这个类中重写流的使用以最小化内存使用?

class Crypto 
{
    Rijndael rijndael;

    public Crypto()
    {
        rijndael = Rijndael.Create();
        rijndael.Key = Encoding.ASCII.GetBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); ;
        rijndael.IV = Encoding.ASCII.GetBytes("bbbbbbbbbbbbbbbb"); ;
        rijndael.Padding = PaddingMode.PKCS7;
    }

    public byte[] Encrypt(object obj)
    {
        var settings = new XmlWriterSettings
            {
                OmitXmlDeclaration = true
            };

        var ns = new XmlSerializerNamespaces();
        ns.Add("", "");

        var sb = new StringBuilder();
        var xmlSerializer = new XmlSerializer(obj.GetType());
        using (var xmlWriter = XmlWriter.Create(sb, settings))
        {
            xmlSerializer.Serialize(xmlWriter, obj, ns);
            xmlWriter.Flush();
        }

        var xmlString = sb.ToString();
        var bytes = Encoding.UTF8.GetBytes(xmlString);
        using (var encryptor = rijndael.CreateEncryptor())
        using (var stream = new MemoryStream())
        using (var crypto = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
        {
            crypto.Write(bytes, 0, bytes.Length);
            crypto.FlushFinalBlock();
            stream.Position = 0;
            var encrypted = new byte[stream.Length];
            stream.Read(encrypted, 0, encrypted.Length);
            return encrypted;
        }
    }

    public T Decrypt<T>(byte[] encryptedValue)
    {
        byte[] decryptedBytes;
        using (var decryptor = rijndael.CreateDecryptor())
        using (var stream = new MemoryStream())
        using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Write))
        {
            crypto.Write(encryptedValue, 0, encryptedValue.Length);
            crypto.FlushFinalBlock();
            stream.Position = 0;
            decryptedBytes = new Byte[stream.Length];
            stream.Read(decryptedBytes, 0, decryptedBytes.Length);
        }

        var ser = new XmlSerializer(typeof(T));

        var decryptedString = Encoding.UTF8.GetString(decryptedBytes);
        using (var stringReader = new StringReader(decryptedString))
        using (var xmlReader = new XmlTextReader(stringReader))
        {
            return (T)ser.Deserialize(xmlReader);

        }
    }

}
Run Code Online (Sandbox Code Playgroud)

这是一个单元测试

[TestFixture]
public class Tests
{
    [Test]
    public void Run()
    {
        var before = new MyClassForSerialize()
            {
                Property = "Sdf"
            };

        var dataEncryptor = new Crypto();
        var encrypted = dataEncryptor.Encrypt(before);
        var after = dataEncryptor.Decrypt<MyClassForSerialize>(encrypted);
        Assert.AreEqual(before.Property, after.Property);
    }
}
public class MyClassForSerialize
{
    public string Property { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

===编辑===

基于Damien_The_Unbeliever的anser,我尝试了这个.哪个单元测试失败了

public byte[] Encrypt(object obj)
{
    var settings = new XmlWriterSettings
    {
        OmitXmlDeclaration = true
    };

    var ns = new XmlSerializerNamespaces();
    ns.Add("", "");
    var xmlSerializer = new XmlSerializer(obj.GetType());

    using (var encryptor = rijndael.CreateEncryptor())
    using (var stream = new MemoryStream())
    using (var crypto = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
    {
        using (var xmlWriter = XmlWriter.Create(crypto, settings))
        {
            xmlSerializer.Serialize(xmlWriter, obj, ns);
            xmlWriter.Flush();
        }
        crypto.FlushFinalBlock();
        stream.Position = 0;
        return stream.ToArray();
    }
}
Run Code Online (Sandbox Code Playgroud)

Dam*_*ver 4

XmlWriter您可以直接在您的顶部构建您的CryptoStream(传递cryptoXmlWriter.Create),而不是使用单独的缓冲区。(解密同上)

并且MemoryStream有一个ToArray方法,因此您不必手动分配、重新定位和读取它。

除此之外,它看起来是一个合理的实现 - 是否有需要解决的具体问题?


根据您的编辑,如果我将解密更改为:

    public T Decrypt<T>(byte[] encryptedValue)
    {
        using (var decryptor = rijndael.CreateDecryptor())
        using (var stream = new MemoryStream(encryptedValue))
            using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Read))
            using (var xmlReader = XmlReader.Create(crypto))
            {
                var ser = new XmlSerializer(typeof(T));
                return (T)ser.Deserialize(xmlReader);

            }
    }
Run Code Online (Sandbox Code Playgroud)

然后它似乎对我有用。


新版本包含 XML BOM,而旧版本则不包含。我原以为XmlReader应该能够应付,但似乎不能。尝试以下设置Encrypt

var settings = new XmlWriterSettings
{
    OmitXmlDeclaration = true,
    Encoding = new UTF8Encoding(false)
};
Run Code Online (Sandbox Code Playgroud)

现在它可以与旧Decrypt功能一起使用。

完整解决方案

加密

public byte[] Encrypt(object obj)
{
    var settings = new XmlWriterSettings
        {
            OmitXmlDeclaration = true,
            Encoding = new UTF8Encoding(false)
        };

    var ns = new XmlSerializerNamespaces();
    ns.Add("", "");

    var xmlSerializer = new XmlSerializer(obj.GetType());
    using (var encryptor = rijndael.CreateEncryptor())
    using (var stream = new MemoryStream())
    using (var crypto = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
    {
        using (var xmlWriter = XmlWriter.Create(crypto, settings))
        {
            xmlSerializer.Serialize(xmlWriter, obj, ns);
            xmlWriter.Flush();
        }
        crypto.FlushFinalBlock();
        return stream.ToArray();
    }
}
Run Code Online (Sandbox Code Playgroud)

解密

public T Decrypt<T>(byte[] encryptedValue)
{
    using (var decryptor = rijndael.CreateDecryptor())
    using (var stream = new MemoryStream(encryptedValue))
    using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Read))
    {
        var ser = new XmlSerializer(typeof(T));
        return (T)ser.Deserialize(crypto);
    }
}
Run Code Online (Sandbox Code Playgroud)