Lev*_*ncz 4 c# cryptography cryptostream .net-4.6.2
我有一个用于加密文本数据的类。我正在尝试尽可能重用ICryptoTransform对象。但是,第二次尝试使用同一对象时,我得到了部分错误解密的数据。我认为第一个块是错误的,但其余的块似乎还可以(使用更长的文本进行了测试)。
我将类简化为以下内容:
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我编写了一个样本使用情况测试来演示该问题:
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 :(");
}
Run Code Online (Sandbox Code Playgroud)
我想念什么?文档表明这些对象是可重用的,但我不知道如何使用。有谁可以帮助我吗?
编辑:
正如@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();
}
Run Code Online (Sandbox Code Playgroud)
然后,可以在主应用程序的app.config中设置此开关,就像@bartonjs的答案一样。
您所缺少的是.NET Framework中的错误(和错误修正):)。
有关此问题,有一个Microsoft Connect问题。特别是AesCryptoServiceProvider.CreateDecryptor()返回一个对象,该对象显示CanReuseTransform=true,但行为似乎不正确。
该错误已在.NET 4.6.2版本中修复,但在重新定位更改后受到了保护。这意味着要查看修复程序,您需要
如果您安装了较新的框架,但希望将可执行文件定位于较低版本的框架,则需要将开关设置Switch.System.Security.Cryptography.AesCryptoServiceProvider.DontCorrectlyResetDecryptor为false。
从AppContext类文档(在“备注”下):
一旦定义并记录了该开关,调用者就可以通过使用注册表,向其应用程序配置文件中添加AppContextSwitchOverrides元素或以编程方式调用AppContext.SetSwitch(String,?Boolean)方法来使用它。
对于配置文件(your.exe.config):
<configuration>
<runtime>
<AppContextSwitchOverrides
value="Switch.System.Security.Cryptography.AesCryptoServiceProvider.DontCorrectlyResetDecryptor=false" />
</runtime>
</configuration>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
840 次 |
| 最近记录: |