当文本少于 15 个字符时,将 CryptoStream 转换为使用声明会使内存流为空
Converting a CryptoStream to using declaration makes memory stream empty when text less than 15 chars
我在使用 C# 加密文本时遇到一个奇怪的问题。
ReSharper(我同意)建议替换此代码中的 using
块:
public object GetEncryptedOrDefault(object value, ICryptoTransform encryptor)
{
if (encryptor is null)
{
throw new ArgumentNullException(nameof(encryptor));
}
var isEncryptionNeeded = value != null;
if (isEncryptionNeeded)
{
using var memoryStream = new MemoryStream();
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
using var writer = new StreamWriter(cryptoStream);
var valueAsText = value.ToString();
writer.Write(valueAsText);
}
var encryptedData = memoryStream.ToArray();
var encryptedText = Convert.ToBase64String(encryptedData);
return encryptedText;
}
return default;
}
到这个简化的(注意使用声明而不是块):
public object GetEncryptedOrDefault(object value, ICryptoTransform encryptor)
{
if (encryptor is null)
{
throw new ArgumentNullException(nameof(encryptor));
}
var isEncryptionNeeded = value != null;
if (isEncryptionNeeded)
{
using var memoryStream = new MemoryStream();
using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
using var writer = new StreamWriter(cryptoStream);
var valueAsText = value.ToString();
writer.Write(valueAsText);
var encryptedData = memoryStream.ToArray();
var encryptedText = Convert.ToBase64String(encryptedData);
return encryptedText;
}
return default;
}
嗯.. 第一个效果很好,它能够加密文本。
但是第二个不起作用! encryptedData
是空的,因此它产生一个空的 encryptedText
.
我看不出问题所在。为什么?
更新 1
多亏了 Emanuel 的回答,我才能够使它仅在要加密的文本大于 15 个字符时才起作用。 这真奇怪。少于 15 个字符,只有带有 "old fashioned" using 块的代码才能工作,而不是使用 using 声明的代码。
我重现了问题in this sample repo at Github。
即使这个问题与 AesManaged(我不知道)有关,为什么第一个方法成功而第二个方法失败 对于任何 15 或更少的文本字符数?
这是代码:
class Program
{
static void Main(string[] args)
{
var encryptor = GetEncryptor();
var text = "Under 15 characters this text causes problems";
while (text.Length >= 0)
{
text = text.Substring(0, text.Length - 1);
Console.WriteLine($"Result Method A with {text.Length} characters: {GetWorkingEncrypted(text, encryptor)}");
Console.WriteLine($"Result Method B with {text.Length} characters: {GetNonWorkingEncrypted(text, encryptor)}");
}
}
private static string GetWorkingEncrypted(string text, ICryptoTransform encryptor)
{
using var memoryStream = new MemoryStream();
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
using var writer = new StreamWriter(cryptoStream);
writer.Write(text);
writer.Flush();
}
var encryptedData = memoryStream.ToArray();
if (encryptedData.Length == 0)
{
throw new Exception($"Encrypted data is 0 for text {text}");
}
var encryptedText = Convert.ToBase64String(encryptedData);
return encryptedText;
}
private static string GetNonWorkingEncrypted(string text, ICryptoTransform encryptor)
{
using var memoryStream = new MemoryStream();
using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
using var writer = new StreamWriter(cryptoStream);
writer.Write(text);
writer.Flush();
var encryptedData = memoryStream.ToArray();
if (encryptedData.Length == 0)
{
throw new Exception($"Encrypted data is 0 for text \"{text}\" with length {text.Length}");
}
var encryptedText = Convert.ToBase64String(encryptedData);
return encryptedText;
}
private static ICryptoTransform GetEncryptor()
{
var aesManaged =
new AesManaged
{
Padding = PaddingMode.PKCS7
};
return aesManaged.CreateEncryptor();
}
}
这是执行的结果:
Result Method A with 44 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3937Peo255iHRylA9DF0lf4K+
Result Method B with 44 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 43 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934Pwaqyce+T6SG3WaqnzNRt
Result Method B with 43 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 42 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3936tIT0560Lky1gz3FXKHU3Y
Result Method B with 42 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 41 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934xr6AiKuSxRet/e8iWhLEV
Result Method B with 41 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 40 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3937gCR2Lf9zQClOlCFw51dVo
Result Method B with 40 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 39 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3936OjZ4HEtzkcIjVMUJcDzum
Result Method B with 39 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 38 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3936ti1b7pskEFKb2zJrRkVaD
Result Method B with 38 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 37 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3937rKO73A+OiHd1aAMqOd3Df
Result Method B with 37 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 36 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934/BNp0BiYZPRMUUiODp/kb
Result Method B with 36 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 35 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3935sjuGp/uE4fVOn26J1ESzH
Result Method B with 35 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 34 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+39360AAj7hDLcnbMZH7aknpDl
Result Method B with 34 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 33 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3935EfO82m/jR81he3Jt4z1h+
Result Method B with 33 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 32 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934zeVj3CoE5YIFK8/g07QmH
Result Method B with 32 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 31 characters: /ppBS775B1KRShB+QKTLZCRCNZXU9Ndp7uKLJkUXFsw=
Result Method B with 31 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 30 characters: /ppBS775B1KRShB+QKTLZJ1WbVjggwJM3uOTZ2dHx5c=
Result Method B with 30 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 29 characters: /ppBS775B1KRShB+QKTLZDiI785bQRbNeZX2aNFQvZo=
Result Method B with 29 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 28 characters: /ppBS775B1KRShB+QKTLZMmLT/ycIHWz0sjPsdfg/ys=
Result Method B with 28 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 27 characters: /ppBS775B1KRShB+QKTLZJDekWQLgx9tTUE/59ldSqs=
Result Method B with 27 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 26 characters: /ppBS775B1KRShB+QKTLZKIkr5xwCc8SS9eSnw715vk=
Result Method B with 26 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 25 characters: /ppBS775B1KRShB+QKTLZFAtZM8oTV/uTBb6OccqErc=
Result Method B with 25 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 24 characters: /ppBS775B1KRShB+QKTLZD5BAXR9qZav1rG5NnaLEQQ=
Result Method B with 24 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 23 characters: /ppBS775B1KRShB+QKTLZFof3ATUQWJqiZ2wZ6Gj4Vc=
Result Method B with 23 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 22 characters: /ppBS775B1KRShB+QKTLZNWhgIhTYyERb74rKEl8bos=
Result Method B with 22 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 21 characters: /ppBS775B1KRShB+QKTLZIsgSoHGJT3XysDLqmV9Bi0=
Result Method B with 21 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 20 characters: /ppBS775B1KRShB+QKTLZO0ZdC9DzISByS5T1Rx4hQ4=
Result Method B with 20 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 19 characters: /ppBS775B1KRShB+QKTLZBFfUwWYJ5ECKF2JexKf8Xk=
Result Method B with 19 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 18 characters: /ppBS775B1KRShB+QKTLZNkZyUqqwkELWI4JN14M2RE=
Result Method B with 18 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 17 characters: /ppBS775B1KRShB+QKTLZOKdO3s345tAlCrN+q3QV68=
Result Method B with 17 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 16 characters: /ppBS775B1KRShB+QKTLZE6HtWd1ZLwZMvy3E9Bm5CI=
Result Method B with 16 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 15 characters: OMMFxti/svtQ/Z5fqaLaEg==
Unhandled exception. System.Exception: Encrypted data is 0 for text "Under 15 charac" with length 15
at IssueEncryptionStreamEmpty.Program.GetNonWorkingEncrypted(String text, ICryptoTransform encryptor) in /media/sasw/Data/src/issue-encryption-stream-empty/Program.cs:line 53
at IssueEncryptionStreamEmpty.Program.Main(String[] args) in /media/sasw/Data/src/issue-encryption-stream-empty/Program.cs:line 17
Process finished with exit code 134.
因为你的 writer
不再局限于 cryptoStream
的 using 块,它现在在你的函数返回之前被处理掉(而不是在 cryptoStream
时被处理掉)范围结束)。但它没有将其内容刷新到流中,因为您没有在其上调用 Flush
,并且其 AutoFlush
属性 默认为 false
.
public object GetEncryptedOrDefault(object value, ICryptoTransform encryptor)
{
if (encryptor is null)
{
throw new ArgumentNullException(nameof(encryptor));
}
var isEncryptionNeeded = value != null;
if (isEncryptionNeeded)
{
using var memoryStream = new MemoryStream();
using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
using var writer = new StreamWriter(cryptoStream);
var valueAsText = value.ToString();
writer.Write(valueAsText);
writer.Flush();
var encryptedData = memoryStream.ToArray();
var encryptedText = Convert.ToBase64String(encryptedData);
return encryptedText;
}
return default;
}
在您的第一个示例中,writer
在用完它写入的流之前被释放(因此刷新其缓冲区),因此在尝试访问它时流不是空的。
更新:
在查看 CryptoStream.Dispose
的源代码时,我注意到对 FlushFinalBlock
的调用,这就是输出不匹配的原因。
关于输入长度问题,encryptor.InputBlockSize
等于16,这就是为什么写15个或更少字符的文本而不调用FlushFinalBlock
的原因(注意CryptoStream.Flush
是无操作)导致空流。
因此,再次工作的代码变为:
private static string GetWorkingEncrypted(string text, ICryptoTransform encryptor)
{
using var memoryStream = new MemoryStream();
using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
using var writer = new StreamWriter(cryptoStream);
writer.Write(text);
writer.Flush();
cryptoStream.FlushFinalBlock();
var encryptedData = memoryStream.ToArray();
var encryptedText = Convert.ToBase64String(encryptedData);
return encryptedText;
}
我在使用 C# 加密文本时遇到一个奇怪的问题。
ReSharper(我同意)建议替换此代码中的 using
块:
public object GetEncryptedOrDefault(object value, ICryptoTransform encryptor)
{
if (encryptor is null)
{
throw new ArgumentNullException(nameof(encryptor));
}
var isEncryptionNeeded = value != null;
if (isEncryptionNeeded)
{
using var memoryStream = new MemoryStream();
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
using var writer = new StreamWriter(cryptoStream);
var valueAsText = value.ToString();
writer.Write(valueAsText);
}
var encryptedData = memoryStream.ToArray();
var encryptedText = Convert.ToBase64String(encryptedData);
return encryptedText;
}
return default;
}
到这个简化的(注意使用声明而不是块):
public object GetEncryptedOrDefault(object value, ICryptoTransform encryptor)
{
if (encryptor is null)
{
throw new ArgumentNullException(nameof(encryptor));
}
var isEncryptionNeeded = value != null;
if (isEncryptionNeeded)
{
using var memoryStream = new MemoryStream();
using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
using var writer = new StreamWriter(cryptoStream);
var valueAsText = value.ToString();
writer.Write(valueAsText);
var encryptedData = memoryStream.ToArray();
var encryptedText = Convert.ToBase64String(encryptedData);
return encryptedText;
}
return default;
}
嗯.. 第一个效果很好,它能够加密文本。
但是第二个不起作用! encryptedData
是空的,因此它产生一个空的 encryptedText
.
我看不出问题所在。为什么?
更新 1 多亏了 Emanuel 的回答,我才能够使它仅在要加密的文本大于 15 个字符时才起作用。 这真奇怪。少于 15 个字符,只有带有 "old fashioned" using 块的代码才能工作,而不是使用 using 声明的代码。
我重现了问题in this sample repo at Github。
即使这个问题与 AesManaged(我不知道)有关,为什么第一个方法成功而第二个方法失败 对于任何 15 或更少的文本字符数?
这是代码:
class Program
{
static void Main(string[] args)
{
var encryptor = GetEncryptor();
var text = "Under 15 characters this text causes problems";
while (text.Length >= 0)
{
text = text.Substring(0, text.Length - 1);
Console.WriteLine($"Result Method A with {text.Length} characters: {GetWorkingEncrypted(text, encryptor)}");
Console.WriteLine($"Result Method B with {text.Length} characters: {GetNonWorkingEncrypted(text, encryptor)}");
}
}
private static string GetWorkingEncrypted(string text, ICryptoTransform encryptor)
{
using var memoryStream = new MemoryStream();
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
using var writer = new StreamWriter(cryptoStream);
writer.Write(text);
writer.Flush();
}
var encryptedData = memoryStream.ToArray();
if (encryptedData.Length == 0)
{
throw new Exception($"Encrypted data is 0 for text {text}");
}
var encryptedText = Convert.ToBase64String(encryptedData);
return encryptedText;
}
private static string GetNonWorkingEncrypted(string text, ICryptoTransform encryptor)
{
using var memoryStream = new MemoryStream();
using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
using var writer = new StreamWriter(cryptoStream);
writer.Write(text);
writer.Flush();
var encryptedData = memoryStream.ToArray();
if (encryptedData.Length == 0)
{
throw new Exception($"Encrypted data is 0 for text \"{text}\" with length {text.Length}");
}
var encryptedText = Convert.ToBase64String(encryptedData);
return encryptedText;
}
private static ICryptoTransform GetEncryptor()
{
var aesManaged =
new AesManaged
{
Padding = PaddingMode.PKCS7
};
return aesManaged.CreateEncryptor();
}
}
这是执行的结果:
Result Method A with 44 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3937Peo255iHRylA9DF0lf4K+
Result Method B with 44 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 43 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934Pwaqyce+T6SG3WaqnzNRt
Result Method B with 43 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 42 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3936tIT0560Lky1gz3FXKHU3Y
Result Method B with 42 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 41 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934xr6AiKuSxRet/e8iWhLEV
Result Method B with 41 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 40 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3937gCR2Lf9zQClOlCFw51dVo
Result Method B with 40 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 39 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3936OjZ4HEtzkcIjVMUJcDzum
Result Method B with 39 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 38 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3936ti1b7pskEFKb2zJrRkVaD
Result Method B with 38 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 37 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3937rKO73A+OiHd1aAMqOd3Df
Result Method B with 37 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 36 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934/BNp0BiYZPRMUUiODp/kb
Result Method B with 36 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 35 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3935sjuGp/uE4fVOn26J1ESzH
Result Method B with 35 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 34 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+39360AAj7hDLcnbMZH7aknpDl
Result Method B with 34 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 33 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3935EfO82m/jR81he3Jt4z1h+
Result Method B with 33 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 32 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934zeVj3CoE5YIFK8/g07QmH
Result Method B with 32 characters: /ppBS775B1KRShB+QKTLZJH/fCQbNFhCvzfbFP+3934=
Result Method A with 31 characters: /ppBS775B1KRShB+QKTLZCRCNZXU9Ndp7uKLJkUXFsw=
Result Method B with 31 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 30 characters: /ppBS775B1KRShB+QKTLZJ1WbVjggwJM3uOTZ2dHx5c=
Result Method B with 30 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 29 characters: /ppBS775B1KRShB+QKTLZDiI785bQRbNeZX2aNFQvZo=
Result Method B with 29 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 28 characters: /ppBS775B1KRShB+QKTLZMmLT/ycIHWz0sjPsdfg/ys=
Result Method B with 28 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 27 characters: /ppBS775B1KRShB+QKTLZJDekWQLgx9tTUE/59ldSqs=
Result Method B with 27 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 26 characters: /ppBS775B1KRShB+QKTLZKIkr5xwCc8SS9eSnw715vk=
Result Method B with 26 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 25 characters: /ppBS775B1KRShB+QKTLZFAtZM8oTV/uTBb6OccqErc=
Result Method B with 25 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 24 characters: /ppBS775B1KRShB+QKTLZD5BAXR9qZav1rG5NnaLEQQ=
Result Method B with 24 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 23 characters: /ppBS775B1KRShB+QKTLZFof3ATUQWJqiZ2wZ6Gj4Vc=
Result Method B with 23 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 22 characters: /ppBS775B1KRShB+QKTLZNWhgIhTYyERb74rKEl8bos=
Result Method B with 22 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 21 characters: /ppBS775B1KRShB+QKTLZIsgSoHGJT3XysDLqmV9Bi0=
Result Method B with 21 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 20 characters: /ppBS775B1KRShB+QKTLZO0ZdC9DzISByS5T1Rx4hQ4=
Result Method B with 20 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 19 characters: /ppBS775B1KRShB+QKTLZBFfUwWYJ5ECKF2JexKf8Xk=
Result Method B with 19 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 18 characters: /ppBS775B1KRShB+QKTLZNkZyUqqwkELWI4JN14M2RE=
Result Method B with 18 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 17 characters: /ppBS775B1KRShB+QKTLZOKdO3s345tAlCrN+q3QV68=
Result Method B with 17 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 16 characters: /ppBS775B1KRShB+QKTLZE6HtWd1ZLwZMvy3E9Bm5CI=
Result Method B with 16 characters: /ppBS775B1KRShB+QKTLZA==
Result Method A with 15 characters: OMMFxti/svtQ/Z5fqaLaEg==
Unhandled exception. System.Exception: Encrypted data is 0 for text "Under 15 charac" with length 15
at IssueEncryptionStreamEmpty.Program.GetNonWorkingEncrypted(String text, ICryptoTransform encryptor) in /media/sasw/Data/src/issue-encryption-stream-empty/Program.cs:line 53
at IssueEncryptionStreamEmpty.Program.Main(String[] args) in /media/sasw/Data/src/issue-encryption-stream-empty/Program.cs:line 17
Process finished with exit code 134.
因为你的 writer
不再局限于 cryptoStream
的 using 块,它现在在你的函数返回之前被处理掉(而不是在 cryptoStream
时被处理掉)范围结束)。但它没有将其内容刷新到流中,因为您没有在其上调用 Flush
,并且其 AutoFlush
属性 默认为 false
.
public object GetEncryptedOrDefault(object value, ICryptoTransform encryptor)
{
if (encryptor is null)
{
throw new ArgumentNullException(nameof(encryptor));
}
var isEncryptionNeeded = value != null;
if (isEncryptionNeeded)
{
using var memoryStream = new MemoryStream();
using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
using var writer = new StreamWriter(cryptoStream);
var valueAsText = value.ToString();
writer.Write(valueAsText);
writer.Flush();
var encryptedData = memoryStream.ToArray();
var encryptedText = Convert.ToBase64String(encryptedData);
return encryptedText;
}
return default;
}
在您的第一个示例中,writer
在用完它写入的流之前被释放(因此刷新其缓冲区),因此在尝试访问它时流不是空的。
更新:
在查看 CryptoStream.Dispose
的源代码时,我注意到对 FlushFinalBlock
的调用,这就是输出不匹配的原因。
关于输入长度问题,encryptor.InputBlockSize
等于16,这就是为什么写15个或更少字符的文本而不调用FlushFinalBlock
的原因(注意CryptoStream.Flush
是无操作)导致空流。
因此,再次工作的代码变为:
private static string GetWorkingEncrypted(string text, ICryptoTransform encryptor)
{
using var memoryStream = new MemoryStream();
using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
using var writer = new StreamWriter(cryptoStream);
writer.Write(text);
writer.Flush();
cryptoStream.FlushFinalBlock();
var encryptedData = memoryStream.ToArray();
var encryptedText = Convert.ToBase64String(encryptedData);
return encryptedText;
}