如何在加密和解密过程中避免额外的内存使用? [英] How to avoid extra memory use during encryption and decryption?

查看:91
本文介绍了如何在加密和解密过程中避免额外的内存使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



现在我觉得这两种方法都有一个额外的字节数组和字符串实例。



xmlString 字节加密





decryptptedString decrypttableBytes 解密



所以如何重新调整此类中的流的使用量以最小化内存使用?

  class Crypto 
{
Rijndael rijndael;

public Crypto()
{
rijndael = Rijndael.Create();
rijndael.Key = Encoding.ASCII.GetBytes(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa); ;
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());
使用(var xmlWriter = XmlWriter.Create(sb,settings))
{
xmlSerializer.Serialize(xmlWriter,obj,ns);
xmlWriter.Flush();
}

var xmlString = sb.ToString();
var bytes = Encoding.UTF8.GetBytes(xmlString);使用(var encryption = new CryptoStream(stream,encryptor,CryptoStreamMode.Write))
使用(var encryptor = rijndael.CreateEncryptor())
使用(var stream = new MemoryStream()
{
crypto.Write(bytes,0,bytes.Length);
crypto.FlushFinalBlock();
stream.Position = 0;
var encrypted = new byte [stream.Length];
stream.Read(encrypted,0,encrypted.Length);
返回加密;
}
}

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

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

var decryptptedString = Encoding.UTF8.GetString(decryptptedBytes);
使用(var stringReader = new StringReader(decryptptedString))
使用(var xmlReader = new XmlTextReader(stringReader))
{
return(T)ser.Deserialize(xmlReader);

}
}

}

这里是单元测试

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

var dataEncryptor = new Crypto();
var encrypted = dataEncryptor.Encrypt(before);
var after = dataEncryptor.Decrypt< MyClassForSerialize>(加密);
Assert.AreEqual(before.Property,after.Property);
}
}
public class MyClassForSerialize
{
public string Property {get;组; }
}

===编辑===



根据Damien_The_Unbeliever的anser,我试过这个。单元测试失败

  public byte []加密(object obj)
{
var settings =新的XmlWriterSettings
{
OmitXmlDeclaration = true
};

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

使用(var encryptor = rijndael.CreateEncryptor())
使用(var stream = new MemoryStream())
使用(var crypto = new CryptoStream(stream,encryptor,CryptoStreamMode使用(var xmlWriter = XmlWriter.Create(crypto,settings))
{
xmlSerializer.Serialize(xmlWriter,obj,ns);
{

xmlWriter.Flush();
}
crypto.FlushFinalBlock();
stream.Position = 0;
return stream.ToArray();
}
}


解决方案

你可以直接在您的 CryptoStream (pass crypto XmlWriter c>到 XmlWriter.Create ),而不是使用单独的缓冲区。 (同上)解密)



MemoryStream 有一个 ToArray 方法,所以你不必手动分配,重新定位和读取它。



除此之外,它看起来像一个合理的实现 - 有具体的问题需要修复?






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

  public T Decrypt< T(byte [] encryptedValue)
{
using(var decryptor = rijndael.CreateDecryptor())$ b $使用(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);

}
}

然后它似乎适用于我






新版本包含一个XML BOM,而旧版本则不包含。 XmlReader 应该应付,我曾经想过,但似乎没有。尝试以下设置在加密

  var settings = new XmlWriterSettings 
{
OmitXmlDeclaration = true,
Encoding = new UTF8Encoding(false)
};

现在它可以与旧的 Decrypt 功能。



完整解决方案



加密

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

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

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

解密

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


So I have a basic crypto class. Note that this is a simplified implementation to illustrate the question.

Now to my mind both these methods have an extra byte array and string instance.

xmlString and bytes in Encrypt

and

decryptedString and decryptedBytes in Decrypt

So how can I rework the usage of streams in this class to minimize the memory usage?

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

        }
    }

}

And here is a unit test

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

=== Edit ===

Based on the anser from Damien_The_Unbeliever I tried this. Which fails the unit test

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

解决方案

You can construct your XmlWriter directly on top of your CryptoStream (pass crypto to XmlWriter.Create), rather than using a separate buffer. (Ditto for decryption)

And MemoryStream has a ToArray method so you don't have to manually allocate, re-position and read from it.

Other than that, it looks like a reasonable implementation - are there specific issues that need fixing?


Based on your edit, if I change decrypt to:

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

            }
    }

Then it seems to work for me.


The new version is including an XML BOM, whereas the old one wasn't. The XmlReader should cope, I'd have thought, but appears not to. Try the following settings in Encrypt:

var settings = new XmlWriterSettings
{
    OmitXmlDeclaration = true,
    Encoding = new UTF8Encoding(false)
};

And now it works with the old Decrypt function.

Full Solution

Encrypt

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

Decrypt

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

这篇关于如何在加密和解密过程中避免额外的内存使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆