.NET Core6/7中UniversalCryptoDecryptor解密密码截断问题求助
RijndaelManaged解密在.NET Core 6/7中仅返回前16字节的修复方案
问题背景
我们的RijndaelManaged解密代码在.NET Core 5中运行正常,但升级到.NET Core 6或7后,解密结果出现截断,只能获取前16字节的明文。相关细节:
- .NET 5使用
Internal.Cryptography.UniversalCryptoDecryptor,.NET 6/7改用System.Security.Cryptography.UniversalCryptoDecryptor后出现解密截断问题。 - 观察到新版本中
Rfc2898DeriveBytes._salt内部缓冲区从16字节扩展到20字节,.NET 5中填充[0 0 0 1],.NET 7中填充[0 0 0 0],但这属于内部实现细节。 - 密钥派生使用HMAC-SHA1算法。
- 由于Azure已停止支持.NET 5,问题修复非常紧急。
问题根源
代码中依赖CryptoStream.Read()一次性读取所有解密字节的方式存在隐患。在.NET 6+的UniversalCryptoDecryptor实现中,Read()方法不会单次返回全部解密数据——尤其是当块大小为256位(32字节)时,仅返回第一个块的16字节是因为读取逻辑的变化,必须循环读取直到流结束,不能依赖单次调用获取完整结果。
另外,RijndaelManaged在.NET 6+中已被标记为过时,若块大小为128位,推荐改用Aes类;如果必须保留Rijndael的256位块大小,需确保读取流的方式正确。
修复后的解密代码
public static string Decrypt(string cipherText, int BLOCKSIZE, int KeySize) { var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText); var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(KeySize / 8).ToArray(); var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(KeySize / 8).Take(KeySize / 8).ToArray(); var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((KeySize / 8) * 2).ToArray(); using (var password = new Rfc2898DeriveBytes(PassPhrase, saltStringBytes, DerivationIterations)) { var keyBytes = password.GetBytes(KeySize / 8); // 若块大小为128,推荐改用Aes.Create(),更符合.NET 6+最佳实践 using (var rijndael = new RijndaelManaged()) { rijndael.BlockSize = BLOCKSIZE; rijndael.Mode = CipherMode.CBC; rijndael.Padding = PaddingMode.PKCS7; using (var decryptor = rijndael.CreateDecryptor(keyBytes, ivStringBytes)) using (var memoryStream = new MemoryStream(cipherTextBytes)) using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) using (var streamReader = new StreamReader(cryptoStream, Encoding.UTF8)) { // 用StreamReader读取完整解密流,避免单次Read的截断问题 return streamReader.ReadToEnd(); } } } }
关键修复点
- 替换
CryptoStream.Read()单次调用为StreamReader.ReadToEnd(),确保读取完整的解密流,适配.NET 6+的解密器逻辑。 - 简化
cipherTextBytes提取逻辑,直接Skip后ToArray()即可,无需额外Take。 - 移除不必要的
Close()调用,using语句会自动释放资源。
如果必须手动处理字节流(而非用StreamReader),可改用循环读取方式:
using (var memoryStream = new MemoryStream(cipherTextBytes)) using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) { List<byte> plainTextBytes = new List<byte>(); int readBytes; byte[] buffer = new byte[1024]; while ((readBytes = cryptoStream.Read(buffer, 0, buffer.Length)) > 0) { plainTextBytes.AddRange(buffer.Take(readBytes)); } return Encoding.UTF8.GetString(plainTextBytes.ToArray()); }
内容的提问来源于stack exchange,提问作者jakob gaardsted
相关产品推荐
相关产品推荐

