C# 使用本机加密提供程序时.NET内存泄漏
我有一个需要加密和解密字符串的应用程序。下面是我的解密方法:C# 使用本机加密提供程序时.NET内存泄漏,c#,.net,memory,encryption,C#,.net,Memory,Encryption,我有一个需要加密和解密字符串的应用程序。下面是我的解密方法: public static string Decrypt(string cipherText) { try { //Decrypt: byte[] keyArray; byte[] toDecryptArray = Convert.FromBase64String(cipherText); keyArray = UTF8Encoding.UTF
public static string Decrypt(string cipherText)
{
try
{
//Decrypt:
byte[] keyArray;
byte[] toDecryptArray = Convert.FromBase64String(cipherText);
keyArray = UTF8Encoding.UTF8.GetBytes(key);
AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
Aes.Key = keyArray;
Aes.Mode = CipherMode.CBC;
Aes.Padding = PaddingMode.PKCS7;
Aes.IV = IV;
ICryptoTransform cTransform = Aes.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);
Aes.Clear();
return UTF8Encoding.UTF8.GetString(resultArray, 0, resultArray.Length);
}
catch (Exception ex)
{
return "FAILED:*" + cipherText + "*" + ex.Message;
}
}
然而,这似乎是泄漏。正如您所看到的,所有变量都是局部作用域,因此在完成此块时应该释放它们。作为背景,我有时几乎不间断地调用这个方法
为了确定是否存在漏洞,我向服务器发送了大量请求。通过仔细跟踪每个请求所使用的每一条代码路径,我确定这段代码是罪魁祸首。当在我的内存中被注释时,使用量会急剧线性上升,而当被注释掉(并简单地返回密文)时,内存使用量会保持平衡
Aes
在finally{}块中,因此即使发生异常,它也会被释放李>
ICryptoTransform
。由于ICryptoTransform
是IDisposable
可以使用语句将其包装在中并进行处理
public static string Decrypt(string cipherText)
{
AesCryptoServiceProvider Aes;
try
{
//Decrypt:
byte[] keyArray;
byte[] toDecryptArray = Convert.FromBase64String(cipherText);
keyArray = UTF8Encoding.UTF8.GetBytes(key);
Aes = new AesCryptoServiceProvider();
Aes.Key = keyArray;
Aes.Mode = CipherMode.CBC;
Aes.Padding = PaddingMode.PKCS7;
Aes.IV = IV;
using (ICryptoTransform cTransform = Aes.CreateDecryptor())
{
byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);
Aes.Clear();
return UTF8Encoding.UTF8.GetString(resultArray, 0, resultArray.Length);
}
}
catch (Exception ex)
{
return "FAILED:*" + cipherText + "*" + ex.Message;
}
finally
{
Aes.Dispose();
}
}
AESCryptServiceProvider实现IDisposable。尝试使用-块在
中使用它。如下所示:
using(AesCryptoServiceProvider Aes = new AesCryptoServiceProvider()){
Aes.Key = keyArray;
Aes.Mode = CipherMode.CBC;
Aes.Padding = PaddingMode.PKCS7;
Aes.IV = IV;
using (ICryptoTransform cTransform = Aes.CreateDecryptor()){
byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);
Aes.Clear();
return UTF8Encoding.UTF8.GetString(resultArray, 0, resultArray.Length);
}
}
通常,您应该始终处理实现IDisposable的对象,例如AESCryptServiceProvider。它们通常使用非托管资源,垃圾收集器不会清除这些资源。所以不是
AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
写
正如其他地方所说,实现IDisposable的对象不会立即被垃圾收集。如果不显式地或通过在using中包装一次性对象来调用Dispose,则该对象将花费更长的时间来收集垃圾
如果你使用一个尝试,你应该考虑在尝试之外声明这些变量,并在最后一个块中实现处理。特别是对于AESCryptServiceProvider,您希望确保即使在发生错误时也执行Clear()方法,因为using不会为您这样做
public static string Decrypt(string cipherText)
{
string decryptedMessage = null;
AesCryptoServiceProvider Aes = null;
ICryptoTransform cTransform = null;
try
{
//Decrypt:
byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
byte[] toDecryptArray = Convert.FromBase64String(cipherText);
AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
Aes.Key = keyArray;
Aes.Mode = CipherMode.CBC;
Aes.Padding = PaddingMode.PKCS7;
Aes.IV = IV;
ICryptoTransform cTransform = Aes.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);
decryptedMessage = UTF8Encoding.UTF8.GetString(resultArray, 0, resultArray.Length);
}
catch (Exception ex)
{
decryptedMessage = "FAILED:*" + cipherText + "*" + ex.Message;
}
finally
{
if (cTransform != null)
{
cTransform.Dispose();
}
if (Aes != null)
{
Aes.Clear();
Aes.Dispose();
}
}
return decryptedMessage;
}
你也应该考虑让异常抛出,通过消除catch块并保持最后,并在这个方法之外处理它。
您还可以返回一个bool作为success/fail,并使用out传递解密后的字符串。这样,您就不会将错误与邮件内容混淆:
public bool string Decrypt(string cipherText, out string decryptedMessage)
{
bool succeeded = false;
decryptedMessage = null;
AesCryptoServiceProvider Aes = null;
ICryptoTransform cTransform = null;
try
{
//Decrypt:
byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
byte[] toDecryptArray = Convert.FromBase64String(cipherText);
AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
Aes.Key = keyArray;
Aes.Mode = CipherMode.CBC;
Aes.Padding = PaddingMode.PKCS7;
Aes.IV = IV;
ICryptoTransform cTransform = Aes.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);
decryptedMessage = UTF8Encoding.UTF8.GetString(resultArray, 0, resultArray.Length);
succeeded = true;
}
catch (Exception ex)
{
decryptedMessage = "FAILED:*" + cipherText + "*" + ex.Message;
}
finally
{
if (cTransform != null)
{
cTransForm.Dispose();
}
if (Aes != null)
{
Aes.Clear();
Aes.Dispose();
}
}
return succeeded;
}
我有一个非常相似的问题,在我应用了讨论中的所有提示后,这个问题并没有消失。这个问题直接指向新的AESCryptServiceProvider()——在这个方法中,它总是在内存不足的情况下崩溃,如果我注释掉这个块(就像在原来的帖子中一样),它就不会发生
事实证明,问题在于我如何组织代码。在对TransformFinalBlock的一次调用中,我将哈希应用于整个字节缓冲区,在我的例子中,它相当大——500000000字节
当我切换到使用多个TransformBlock和TransformFinalBlock的组合时,一切都很好。下面是我的最后一段代码(也许您会找到一种更优雅的方法来编写循环,其中TransformFinalBlock只在流的最后一部分调用一次):
var bytes=新字节[4*1024];
int lastPortionSize;
使用(var cryptographer=new SHA256CryptoServiceProvider()){
var字节数=0;
while(true){
lastPortionSize=sourceStream.Read(字节,0,字节.长度);
字节数+=最后的端口大小;
if(字节数<总大小){
TransformBlock(字节,0,lastPortionSize,字节,0);
}否则{
打破
}
}
TransformFinalBlock(字节,0,lastPortionSize);
var hashBytes=cryptographer.Hash;
}
返回ex.Message的错误做法。让异常传播,这样您就一定知道发生了什么不好的事情。如果AESCryptServiceProvider和转换被包装在using()中,您会得到泄漏吗?@JohnSaunders是的。一切都处于日志记录/调试模式,试图查明我的问题。您是否100%确定此方法存在内存泄漏?你用什么来分析内存泄漏?您是否尝试过在返回值之前强制清除此方法中的局部变量?你做了什么来验证这个问题?它很难捕捉到这样一个简单的bug,它将存在于AsSuthSotoCeVIEW()中,考虑它的用法和年龄。我想暗示的是,这不太可能是您的泄漏源。@kmark2-如何处理返回值。我怀疑在这种方法之前。因为它真的什么都不是。我还建议使用语句。因此,请更新您的代码,分析更新后的代码,然后报告。请使用适当的内存配置文件软件。ICryptoTransform还实现IDisposable,因此需要将其包装为well@PeskyGnat:好的,我已经插入了这个-谢谢。
public bool string Decrypt(string cipherText, out string decryptedMessage)
{
bool succeeded = false;
decryptedMessage = null;
AesCryptoServiceProvider Aes = null;
ICryptoTransform cTransform = null;
try
{
//Decrypt:
byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
byte[] toDecryptArray = Convert.FromBase64String(cipherText);
AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
Aes.Key = keyArray;
Aes.Mode = CipherMode.CBC;
Aes.Padding = PaddingMode.PKCS7;
Aes.IV = IV;
ICryptoTransform cTransform = Aes.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);
decryptedMessage = UTF8Encoding.UTF8.GetString(resultArray, 0, resultArray.Length);
succeeded = true;
}
catch (Exception ex)
{
decryptedMessage = "FAILED:*" + cipherText + "*" + ex.Message;
}
finally
{
if (cTransform != null)
{
cTransForm.Dispose();
}
if (Aes != null)
{
Aes.Clear();
Aes.Dispose();
}
}
return succeeded;
}
var bytes = new byte[4 * 1024];
int lastPortionSize;
using (var cryptographer = new SHA256CryptoServiceProvider()) {
var byteCount = 0;
while (true) {
lastPortionSize = sourceStream.Read(bytes, 0, bytes.Length);
byteCount += lastPortionSize;
if (byteCount < totalSize) {
cryptographer.TransformBlock(bytes, 0, lastPortionSize, bytes, 0);
} else {
break;
}
}
cryptographer.TransformFinalBlock(bytes, 0, lastPortionSize);
var hashBytes = cryptographer.Hash;
}