我使用了一个字符串加密/解密类,类似于提供的这里作为解决方案。
在.Net 5中,这对我很有用。
现在,我想将我的项目更新为.Net 6。
使用.Net 6时,根据输入字符串的长度,解密的字符串确实会被切断某个点。
为了简化调试/重现问题,我创建了一个公共Repository 这里。
两者都使用完全相同的"12345678901234567890"输入和"nzv86ri4H2qYHqc&m6rL"的路径短语调用加密方法。
.Net 5输出:"12345678901234567890"
.Net 6输出:"1234567890123456"
长度的差异是4。
我也看了看.Net 6的中断更改,但是找不到一些东西来指导我找到解决方案。
我很高兴有任何关于我的问题的建议,谢谢!
加密类
public static class StringCipher
{
// This constant is used to determine the keysize of the encryption algorithm in bits.
// We divide this by 8 within the code below to get the equivalent number of bytes.
private const int Keysize = 128;
// This constant determines the number of iterations for the password bytes generation function.
private const int DerivationIterations = 1000;
public static string Encrypt(string plainText, string passPhrase)
{
// Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
// so that the same Salt and IV values can be used when decrypting.
var saltStringBytes = Generate128BitsOfRandomEntropy();
var ivStringBytes = Generate128BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = Aes.Create())
{
symmetricKey.BlockSize = 128;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
// Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}
public static string Decrypt(string cipherText, string passPhrase)
{
// Get the complete stream of bytes that represent:
// [32 bytes of Salt] + [16 bytes of IV] + [n bytes of CipherText]
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 16 bytes from the supplied cipherText bytes.
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 16 bytes from the supplied cipherText bytes.
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = Aes.Create())
{
symmetricKey.BlockSize = 128;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream(cipherTextBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
}
}
}
}
private static byte[] Generate128BitsOfRandomEntropy()
{
var randomBytes = new byte[16]; // 16 Bytes will give us 128 bits.
using (var rngCsp = RandomNumberGenerator.Create())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
}呼叫码
var input = "12345678901234567890";
var inputLength = input.Length;
var inputBytes = Encoding.UTF8.GetBytes(input);
var encrypted = StringCipher.Encrypt(input, "nzv86ri4H2qYHqc&m6rL");
var output = StringCipher.Decrypt(encrypted, "nzv86ri4H2qYHqc&m6rL");
var outputLength = output.Length;
var outputBytes = Encoding.UTF8.GetBytes(output);
var lengthDiff = inputLength - outputLength;发布于 2021-11-10 10:23:20
原因是这一巨大的变化
DeflateStream、GZipStream和CryptoStream在以下两方面偏离了典型的Stream.Read和Stream.ReadAsync行为: 他们没有完成读操作,直到传递给读操作的缓冲区被完全填充或者流的末尾到达为止。
新的行为是:
从.NET 6开始,当对一个长度为N的受影响流类型调用Stream.Read或Stream.ReadAsync时,操作将在以下情况下完成: 至少已经从流中读取了一个字节,或者它们包装的底层流从对其read的调用中返回0,表明没有更多的数据可用。
在您的示例中,由于Decrypt方法中的此代码,您受到了影响:
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}您不需要检查Read实际读取了多少字节,以及它是否全部读取了它们。在.NET的早期版本中,您可以避免这种情况,因为正如前面提到的那样,CryptoStream行为与其他流不同,而且您的缓冲区长度足以容纳所有数据。但是,这种情况不再是这样,您需要像检查其他流一样检查它。或者更好--只需使用CopyTo
using (var plainTextStream = new MemoryStream())
{
cryptoStream.CopyTo(plainTextStream);
var plainTextBytes = plainTextStream.ToArray();
return Encoding.UTF8.GetString(plainTextBytes, 0, plainTextBytes.Length);
} 或者更好的答案是,因为您解密了UTF8文本:
using (var plainTextReader = new StreamReader(cryptoStream))
{
return plainTextReader.ReadToEnd();
} 发布于 2021-11-10 10:23:40
我想你的问题就在这里:
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);来自文档
即使流的末尾尚未到达,也可以自由地返回比请求的字节少的实现。
因此,对Read的单个调用不能保证读取所有可用字节(直到plainTextBytes.Length) --读取较小数量的字节完全符合其权限。
.NET 6有许多性能改进,如果这是他们以性能为名进行权衡的话,我也不会感到惊讶。
您必须做得很好,并一直调用Read,直到它返回0为止,这意味着没有更多的数据要返回。
但是,只使用StreamReader要容易得多,这也将为您处理UTF-8解码。
return new StreamReader(cryptoStream).ReadToEnd();发布于 2022-05-26 15:41:42
我在我的.net6项目中使用了这两个扩展方法。
namespace WebApi.Utilities;
public static class StringUtil
{
static string key = "Mohammad-Komaei@Encrypt!keY#";
public static string Encrypt(this string text)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key must have valid value.", nameof(key));
if (string.IsNullOrEmpty(text))
throw new ArgumentException("The text must have valid value.", nameof(text));
var buffer = Encoding.UTF8.GetBytes(text);
var hash = SHA512.Create();
var aesKey = new byte[24];
Buffer.BlockCopy(hash.ComputeHash(Encoding.UTF8.GetBytes(key)), 0, aesKey, 0, 24);
using (var aes = Aes.Create())
{
if (aes == null)
throw new ArgumentException("Parameter must not be null.", nameof(aes));
aes.Key = aesKey;
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (var resultStream = new MemoryStream())
{
using (var aesStream = new CryptoStream(resultStream, encryptor, CryptoStreamMode.Write))
using (var plainStream = new MemoryStream(buffer))
{
plainStream.CopyTo(aesStream);
}
var result = resultStream.ToArray();
var combined = new byte[aes.IV.Length + result.Length];
Array.ConstrainedCopy(aes.IV, 0, combined, 0, aes.IV.Length);
Array.ConstrainedCopy(result, 0, combined, aes.IV.Length, result.Length);
return Convert.ToBase64String(combined);
}
}
}
public static string Decrypt(this string encryptedText)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key must have valid value.", nameof(key));
if (string.IsNullOrEmpty(encryptedText))
throw new ArgumentException("The encrypted text must have valid value.", nameof(encryptedText));
var combined = Convert.FromBase64String(encryptedText);
var buffer = new byte[combined.Length];
var hash = SHA512.Create();
var aesKey = new byte[24];
Buffer.BlockCopy(hash.ComputeHash(Encoding.UTF8.GetBytes(key)), 0, aesKey, 0, 24);
using (var aes = Aes.Create())
{
if (aes == null)
throw new ArgumentException("Parameter must not be null.", nameof(aes));
aes.Key = aesKey;
var iv = new byte[aes.IV.Length];
var ciphertext = new byte[buffer.Length - iv.Length];
Array.ConstrainedCopy(combined, 0, iv, 0, iv.Length);
Array.ConstrainedCopy(combined, iv.Length, ciphertext, 0, ciphertext.Length);
aes.IV = iv;
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
using (var resultStream = new MemoryStream())
{
using (var aesStream = new CryptoStream(resultStream, decryptor, CryptoStreamMode.Write))
using (var plainStream = new MemoryStream(ciphertext))
{
plainStream.CopyTo(aesStream);
}
return Encoding.UTF8.GetString(resultStream.ToArray());
}
}
}
}https://stackoverflow.com/questions/69911084
复制相似问题