
我将类简化为以下内容:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace Sample.Crypto
{
public class EncryptedStreamResolver : IDisposable
{
private AesCryptoServiceProvider _cryptoProvider;
private ICryptoTransform _encryptorTransform;
private ICryptoTransform _decryptorTransform;
private ICryptoTransform EncryptorTransform
{
get
{
if (null == _encryptorTransform || !_encryptorTransform.CanReuseTransform)
{
_encryptorTransform?.Dispose();
_encryptorTransform = _cryptoProvider.CreateEncryptor();
}
return _encryptorTransform;
}
}
private ICryptoTransform DecryptorTransform
{
get
{
if (null == _decryptorTransform || !_decryptorTransform.CanReuseTransform)
{
_decryptorTransform?.Dispose();
_decryptorTransform = _cryptoProvider.CreateDecryptor();
}
return _decryptorTransform;
}
}
public EncryptedStreamResolver()
{
GenerateCryptoProvider();
}
public Stream OpenRead(string rawPath)
{
return new CryptoStream(File.OpenRead(rawPath + ".crypto"), DecryptorTransform, CryptoStreamMode.Read);
}
public Stream OpenWrite(string rawPath)
{
return new CryptoStream(File.OpenWrite(rawPath + ".crypto"), EncryptorTransform, CryptoStreamMode.Write);
}
private void GenerateCryptoProvider(string password = "totallysafepassword")
{
_cryptoProvider = new AesCryptoServiceProvider();
_cryptoProvider.BlockSize = _cryptoProvider.LegalBlockSizes.Select(ks => ks.MaxSize).Max();
_cryptoProvider.KeySize = _cryptoProvider.LegalKeySizes.Select(ks => ks.MaxSize).Max();
_cryptoProvider.IV = new byte[_cryptoProvider.BlockSize / 8];
_cryptoProvider.Key = new byte[_cryptoProvider.KeySize / 8];
var pwBytes = Encoding.UTF8.GetBytes(password);
for (var i = 0; i < _cryptoProvider.IV.Length; i++)
_cryptoProvider.IV[i] = pwBytes[i % pwBytes.Length];
for (var i = 0; i < _cryptoProvider.Key.Length; i++)
_cryptoProvider.Key[i] = pwBytes[i % pwBytes.Length];
}
public void Dispose()
{
_encryptorTransform?.Dispose();
_decryptorTransform?.Dispose();
_cryptoProvider?.Dispose();
}
}
}
我编写了一个样本使用情况测试来演示该问题:
public void Can_reuse_encryptor()
{
const string message = "Secret corporate information here.";
const string testFilePath1 = "Foo1.xml";
const string testFilePath2 = "Foo2.xml";
var sr = new EncryptedStreamResolver();
// Write secret data to file
using (var writer = new StreamWriter(sr.OpenWrite(testFilePath1)))
writer.Write(message);
// Read it back and compare with original message
using (var reader = new StreamReader(sr.OpenRead(testFilePath1)))
if (!message.Equals(reader.ReadToEnd()))
throw new Exception("This should never happend :(");
// Write the same data again to a different file
using (var writer = new StreamWriter(sr.OpenWrite(testFilePath2)))
writer.Write(message);
// Read that back and compare
using (var reader = new StreamReader(sr.OpenRead(testFilePath2)))
if (!message.Equals(reader.ReadToEnd()))
throw new Exception("This should never happend :(");
}
我想念什么?该文档表明这些对象是可重用的,但我不知道如何使用.有人能帮助我吗?
编辑:
正如@bartonjs指出的那样,如果我将包含以上代码的项目重新定位到.NET 4.6(或更高版本),则可以使用System.AppContext.TryGetSwitch像这样:
var reuseTransform = false;
if (null == _decryptorTransform ||
!(AppContext.TryGetSwitch("Switch.System.Security.Cryptography.AesCryptoServiceProvider.DontCorrectlyResetDecryptor", out reuseTransform) && reuseTransform && _decryptorTransform.CanReuseTransform))
{
_decryptorTransform?.Dispose();
_decryptorTransform = _cryptoProvider.Createdecryptor();
}
然后,可以在主应用程序的app.config中设置此开关,就像@bartonjs的答案一样.
这个问题大约有Microsoft Connect Issue.特别是AesCryptoServiceProvider.CreateDecryptor()返回的对象说CanReuseTransform = true,但似乎行为不正确.
该错误已在.NET 4.6.2发行版中进行了修复,但在retargeting change之后进行了保护.这意味着要查看此修复程序,您需要
>安装.NET Framework 4.6.2或更高版本.
>将您的主要可执行文件的最低框架版本更改为4.6.2或更高.
如果您安装了较新的框架,但希望将可执行文件定位于框架的较低版本,则需要将Switch.System.Security.Cryptography.AesCryptoServiceProvider.DontCorrectlyResetDecryptor设置为false.
从AppContext class documentation(在“备注”下):
Once you define and document the switch, callers can use it by using the registry, by adding an AppContextSwitchOverrides element to their application configuration file, or by calling the 07003 method programmatically.
对于配置文件(your.exe.config):
<configuration>
<runtime>
<AppContextSwitchOverrides
value="Switch.System.Security.Cryptography.AesCryptoServiceProvider.DontCorrectlyResetDecryptor=false" />
</runtime>
</configuration>
转载注明原文:重复使用ICryptoTransform对象 - 乐贴网