我正在使用類似于此處提供的字串加密/解密類作為解決方案。
這在 .Net 5 中對我很有效。
現在我想將我的專案更新到 .Net 6。
使用 .Net 6 時,解密的字串確實會根據輸入字串的長度在某個點被截斷。
??為了便于除錯/重現我的問題,我在這里創建了一個公共重現存盤庫。
- 加密代碼是在標準 2.0 專案中故意使用的。
- 參考這個專案是一個 .Net 6 和一個 .Net 5 控制臺專案。
兩者都使用"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;
uj5u.com熱心網友回復:
原因是這種重大變化:
DeflateStream、GZipStream 和 CryptoStream 在兩個方面不同于典型的 Stream.Read 和 Stream.ReadAsync 行為:
直到傳遞給讀取操作的緩沖區完全填滿或到達流的末尾,它們才完成讀取操作。
新行為是:
從 .NET 6 開始,當在緩沖區長度為 N 的受影響流型別之一上呼叫 Stream.Read 或 Stream.ReadAsync 時,操作在以下情況下完成:
至少已從流中讀取了一個位元組,或者它們包裝的底層流從對其讀取的呼叫回傳 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();
}
uj5u.com熱心網友回復:
我認為你的問題在這里:
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
從Stream.Read檔案:
即使尚未到達流的末尾,實作也可以自由地回傳比請求少的位元組。
因此,單個呼叫Read不能保證讀取所有可用位元組(最多plainTextBytes.Length- 讀取較少數量的位元組完全在其權利范圍內。
.NET 6 有許多性能改進,如果這是他們以性能的名義進行的權衡,我不會感到驚訝。
您必須保持良好狀態,并繼續呼叫Read直到它回傳0,這表明沒有更多資料要回傳。
但是,僅使用 aStreamReader會容易得多,它還將為您處理 UTF-8 解碼。
return new StreamReader(cryptoStream).ReadToEnd();
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/355961.html
上一篇:根據條件在.netcore中的var中插入物件串列/自定義模型串列
下一篇:在聚合之外強制執行業務規則
