Не работает метод дешифрования C#
Здравствуйте нужно зашифровать данные путей к файлам, использую вот такой метод
static public object EncryptData(byte[] data, string password)
{
SymmetricAlgorithm sa = null;
try
{
sa = Rijndael.Create();
ICryptoTransform ct = sa.CreateEncryptor(
(new PasswordDeriveBytes(password, null)).GetBytes(16),
new byte[16]);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, ct, CryptoStreamMode.Write);
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
catch (Exception e)
{
return e;
}
}
static public string EncryptDataStr(string data, string password)
{
object tmp = EncryptData(Encoding.UTF8.GetBytes(data), password);
if (!(tmp is Exception))
{
return Convert.ToBase64String((byte[])tmp);
}
else return null;
}
так вот, шифрование работает, но метод дешифрования не работает. Метод дешифрования
static public object DecryptData(byte[] data, string password)
{
SymmetricAlgorithm sa = null;
try
{
sa = Rijndael.Create();
ICryptoTransform ct = sa.CreateDecryptor(
(new PasswordDeriveBytes(password, null)).GetBytes(16),
new byte[16]);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, ct, CryptoStreamMode.Read);
cs.Read(data, 0, data.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
catch (Exception e)
{
return e;
}
}
static public string DecryptDataStr(string data, string password)
{
object tmp = DecryptData(Encoding.UTF8.GetBytes(data), password);
if (!(tmp is Exception))
{
return Convert.ToBase64String((byte[])tmp);
}
else return null;
}
Метод дешифровки выдаёт ошибку System.Security.Cryptography.CryptographicException: "Заполнение неверно и не может быть удалено.", в чём может быть проблема?. Я новичок, поэтому прошу прощения если проблема очень лёгкая.
Ответы (1 шт):
CryptographicException: Заполнение неверно и не может быть удалено. или на английском CryptographicException: Padding is invalid and cannot be removed. означает, что либо ключ шифрования (пароль) неверен, либо баги в коде.
Ну давайте разбираться, выглядит как код, откопанный из 2000-x, здесь устарело всё.
И так:
Rijndael- дырявое старьё, закопайте обратно, есть жеAes.PasswordDeriveBytes- дырявое старьё, закопайте обратно, есть жеRfc2898DeriveBytes.- исключения должен обрабатывать вызывающий код, а не вот это вот всё.
- забыли про Initial Vector (IV)
- забыли про
IDisposable - почему-то
DecryptDataStrничем не отличается отEncryptDataStr. Думаете, если скопировать и переименовать шифрующий метод, он будет расшифровывать? Не будет. Не смутило даже 2 разаToBase64String, а где обратное преобразованиеFromBase64String?
Вот, немного переписал, использую рандомный IV и сохраняю его вместе с данными. А так же, чтобы не мудрить, его же использую как соль к паролю. По умолчанию Rfc2898DeriveBytes использует хэширование SHA1, которое уже тоже устарело, поэтому заменил на более модный алгоритм SHA256.
Пусть вас не смущает число 1000, передаваемое в конструктор, это значение по умолчанию, означающее, сколько раз надо прокрутить пароль через хэш, чтобы получить результат. Просто у Rfc2898DeriveBytes нет конструктора, который принимает имя алгоритма хэширования без указания количества итераций.
public static byte[] EncryptData(byte[] data, string password)
{
using SymmetricAlgorithm sa = Aes.Create();
using Rfc2898DeriveBytes hasher = new(password, sa.IV, 1000, HashAlgorithmName.SHA256);
sa.Key = hasher.GetBytes(32);
using MemoryStream ms = new();
ms.Write(sa.IV);
using (CryptoStream cs = new(ms, sa.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(data);
}
return ms.ToArray();
}
public static string EncryptText(string text, string password)
{
return Convert.ToBase64String(EncryptData(Encoding.UTF8.GetBytes(text), password));
}
public static byte[] DecryptData(byte[] data, string password)
{
using MemoryStream ms = new(data);
byte[] iv = new byte[16];
ms.Read(iv);
using SymmetricAlgorithm sa = Aes.Create();
using Rfc2898DeriveBytes hasher = new(password, iv, 1000, HashAlgorithmName.SHA256);
sa.Key = hasher.GetBytes(32);
sa.IV = iv;
using CryptoStream cs = new(ms, sa.CreateDecryptor(), CryptoStreamMode.Read, true);
using MemoryStream output = new();
cs.CopyTo(output);
return output.ToArray();
}
public static string DecryptText(string text, string password)
{
return Encoding.UTF8.GetString(DecryptData(Convert.FromBase64String(text), password));
}
А вот проверяющий код, обработка исключений здесь
static void Main(string[] args)
{
string text = "Hello World";
string password = "1234";
string base64 = null;
try
{
base64 = EncryptText(text, password);
Console.WriteLine(base64);
}
catch (Exception ex)
{
Console.WriteLine($"Не удалось зашифровать ({ex.GetType().Name}: {ex.Message})");
}
try
{
string decrypted = DecryptText(base64, password);
Console.WriteLine(decrypted);
}
catch (Exception ex)
{
Console.WriteLine($"Не удалось расшифровать ({ex.GetType().Name}: {ex.Message})");
}
}
Вывод в консоль
Rz7u8DBJeSngbO/D2bvicpfUTfUaHtsZ9zy9RKZg3eQ=
Hello World
Кстати, при каждом запуске вывод другой, но всё работает, так и должно быть
vJHiX4OctBm0C9QFXB2YaFj4PROTYLbBgky1c9tYuzY=
Hello World
А вот что будет, если указать неверный пароль при расшифровке
S4O4E5J/cgMAJh7oFK4NhD1Br1W8yc1CelgjB59KD9M=
Не удалось расшифровать (CryptographicException: Padding is invalid and cannot be removed.)
Знакомая ошибка?