我正在将一个较旧的应用程序移植到.Net 6,并且遇到了加密/解密方法正在失败的绊脚石。它在.Net 4.x.x下仍然工作得很好。
抛出的错误是,
"Padding is invalid and cannot be removed."
代码:-更新为实际的原始代码。此代码在以.Net 4.7.2为目标时运行良好,但是在将代码移动到.Net 6.0 RC2后,它开始丢失任何超过32个字符的解密字符串,这会导致其他地方出现错误,因为字符串不完整。
获取上下文信息。这是在网络主机和桌面客户端上运行的,用于加密传输中的消息。已更新并验证.Net主机是否发送了正确的加密值(使用.Net 4客户端解密邮件成功)。但是,.Net 6桌面客户端无法正确解密它,并且丢失了解密字符串中的字符。
#region Encrypt method(s)
private const int Keysize = 256;
private const int Blocksize = 128;
private const int DerivationIterations = 1000;
public async Task<string> EncryptStringWithValidatedPadding(string plainText, string passPhrase)
{
string encrypted = null;
bool valid = false;
while (!valid)
{
encrypted = await Encrypt(plainText, passPhrase);
if (!string.IsNullOrEmpty(await Decrypt(encrypted, passPhrase)))
{
valid = true;
}
}
return encrypted;
}
private async Task<string> Encrypt(string plainText, string passPhrase)
{
var saltStringBytes = GenerateRandomEntropy(32); // 256 bits
var ivStringBytes = GenerateRandomEntropy(16); // 128 bits
byte[] plainTextBytes = Convert.FromBase64String(plainText);
using (var password = new Rfc2898DeriveBytes(Convert.FromBase64String(passPhrase), saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new AesManaged())
{
symmetricKey.KeySize = Keysize;
symmetricKey.BlockSize = Blocksize;
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();
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
var encrypted64String = Convert.ToBase64String(cipherTextBytes);
return encrypted64String;
}
}
}
}
}
}
private static byte[] GenerateRandomEntropy(int byteSize)
{
var randomBytes = new byte[byteSize];
using (var rngCsp = new RNGCryptoServiceProvider())
{
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
#endregion
#region Decrypt method
public static async Task<string> Decrypt(string cipherText, string passPhrase)
{
try
{
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Blocksize / 8).ToArray();
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) + Blocksize / 8).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) + Blocksize / 8)).ToArray();
using (var password = new Rfc2898DeriveBytes(Convert.FromBase64String(passPhrase), saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new AesManaged())
{
symmetricKey.KeySize = 256;
symmetricKey.BlockSize = Blocksize;
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);
}
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
#endregion
这用来调用,
encryptedString = await new EncryptDecrypt().EncryptStringWithValidatedPadding(b64String, Convert.ToBase64String(Encoding.UTF8.GetBytes(passPhrase)));
我假设拯救静脉输液应该可以解决这个问题,但我想知道这里是否有任何明显的缺陷,我只是没有看到。
有人能解释一下吗?
更新:按照建议,我已经将代码重构为以下代码。为了确保底层算法的正常工作,我还将其剥离了一分钟。
参考:https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.aes?view=net-6.0
namespace Encryption_Helper
{
public class EncryptDecrypt
{
#region Encrypt method(s)
private static byte[] bytes = new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 };
private const int Keysize = 256;
private const int Blocksize = 128;
private const int DerivationIterations = 1000;
public static async Task<string> EncryptStringWithValidatedPadding(string plainText, string passPhrase)
{
string encrypted = null;
bool valid = false;
while (!valid)
{
encrypted = await Encrypt(plainText, passPhrase);
if (!string.IsNullOrEmpty(await Decrypt(encrypted, passPhrase)))
{
valid = true;
}
}
return encrypted;
}
private static async Task<string> Encrypt(string plainText, string passPhrase)
{
using (var password = new Rfc2898DeriveBytes(Convert.FromBase64String(passPhrase), bytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
var ivBytes = password.GetBytes(Blocksize / 8);
using (var aes = Aes.Create())
{
aes.Key = keyBytes;
aes.IV = ivBytes;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
plainText = Convert.ToBase64String(msEncrypt.ToArray());
}
}
}
return plainText;
}
}
#endregion
#region Decrypt method
public static async Task<string> Decrypt(string cipherText, string passPhrase)
{
try
{
using (var password = new Rfc2898DeriveBytes(Convert.FromBase64String(passPhrase), bytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
var ivBytes = password.GetBytes(Blocksize / 8);
using (var aes = Aes.Create())
{
aes.Key = keyBytes;
aes.IV = ivBytes;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (var memoryStream = new MemoryStream(Convert.FromBase64String(cipherText)))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(cryptoStream))
{
cipherText = srDecrypt.ReadToEnd();
}
}
}
return cipherText;
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
#endregion
}
}
它仍然抛出填充错误!
发布于 2021-10-22 19:36:33
解决了!
public class EncryptDecrypt
{
#region Encrypt method(s)
private const int Keysize = 256;
private const int Blocksize = 128;
private const int DerivationIterations = 1000;
public static async Task<string> EncryptStringWithValidatedPadding(string plainText, string passPhrase)
{
string encrypted = null;
bool valid = false;
while (!valid)
{
encrypted = await Encrypt(plainText, passPhrase);
if (!string.IsNullOrEmpty(await Decrypt(encrypted, passPhrase)))
{
valid = true;
}
}
return encrypted;
}
private static async Task<string> Encrypt(string plainText, string passPhrase)
{
var saltStringBytes = GenerateRandomEntropy(Keysize / 8); // 256 bits
var ivStringBytes = GenerateRandomEntropy(Blocksize / 8); // 128 bits
byte[] plainTextBytes = Convert.FromBase64String(plainText);
using (var password = new Rfc2898DeriveBytes(Convert.FromBase64String(passPhrase), saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var aes = Aes.Create())
{
aes.KeySize = Keysize;
aes.BlockSize = Blocksize;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(keyBytes, ivStringBytes), CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
var encrypted64String = Convert.ToBase64String(cipherTextBytes);
return encrypted64String;
}
}
}
}
}
private static byte[] GenerateRandomEntropy(int byteSize)
{
var randomBytes = new byte[byteSize];
using (var rngCsp = new RNGCryptoServiceProvider())
{
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
#endregion
#region Decrypt method
public static async Task<string> Decrypt(string cipherText, string passPhrase)
{
try
{
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Blocksize / 8).ToArray();
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) + Blocksize / 8).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) + Blocksize / 8)).ToArray();
using (var password = new Rfc2898DeriveBytes(Convert.FromBase64String(passPhrase), saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var aes = Aes.Create())
{
aes.KeySize = Keysize;
aes.BlockSize = Blocksize;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
using (var ms = new MemoryStream(cipherTextBytes))
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(keyBytes, ivStringBytes), CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(cs))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
cipherText = srDecrypt.ReadToEnd();
}
}
}
return cipherText;
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
#endregion
更新解密方法后,世界上一切又好起来了。
在我看来,.Net 6打破了嵌套的using
循环,在完全设置返回值之前关闭了流。
https://stackoverflow.com/questions/69679484
复制相似问题