#c# #encryption #rijndaelmanaged
#c# #шифрование #криптография #rijndael
Вопрос:
Я работаю в приложении C #. У нас есть общие методы для хранения данных в файле. Эти методы шифруют данные и сохраняют их в файловой системе. когда нам нужны данные, метод ReadData расшифровывает данные и возвращает мне обычный текст.
Этот код отлично работает в обычных случаях, если размер текста небольшой. но для примера текста, приведенного ниже, код дешифрования вызывает исключение — длина данных для дешифрования недопустима.
Исключение возникает в строке
// close the CryptoStream
x_cryptostream.Close();
Я пробовал разные способы, но безуспешно. Может ли кто-нибудь помочь.
Почему я шифрую уже зашифрованные данные — я просто пытаюсь сохранить в файле, используя обычный метод огромного приложения. Обычные методы storedata(key,data)
nad readdata(key)
выполняют шифрование / дешифрование, которых я не могу избежать.
public static byte[] Decrypt(byte[] ciphertext, string Key, string IV)
{
byte[] k = Encoding.Default.GetBytes(Key);
byte[] iv = Encoding.Default.GetBytes(IV);
// create the encryption algorithm
SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");
x_alg.Padding = PaddingMode.PKCS7;
// create an ICryptoTransform that can be used to decrypt data
ICryptoTransform x_decryptor = x_alg.CreateDecryptor(k, iv);
// create the memory stream
MemoryStream x_memory_stream = new MemoryStream();
// create the CryptoStream that ties together the MemoryStream and the
// ICryptostream
CryptoStream x_cryptostream = new CryptoStream(x_memory_stream,
x_decryptor, CryptoStreamMode.Write);
// write the ciphertext out to the cryptostream
x_cryptostream.Write(ciphertext, 0, ciphertext.Length);
// close the CryptoStream
x_cryptostream.Close();
// get the plaintext from the MemoryStream
byte[] x_plaintext = x_memory_stream.ToArray();
Ниже приведен код метода шифрования.
public static byte[] Encrypt(string strplain, string Key, string IV)
{
byte[] k = Encoding.Default.GetBytes(Key);
byte[] iv = Encoding.Default.GetBytes(IV);
byte[] plaintext = Encoding.Default.GetBytes(strplain);
// create the encryption algorithm
SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");
x_alg.Padding = PaddingMode.PKCS7;
// create an ICryptoTransform that can be used to encrypt data
ICryptoTransform x_encryptor = x_alg.CreateEncryptor(k, iv);
// create the memory stream
MemoryStream x_memory_stream = new MemoryStream();
// create the CryptoStream that ties together the MemoryStream and
// the ICryptostream
CryptoStream x_cryptostream = new CryptoStream(x_memory_stream,
x_encryptor, CryptoStreamMode.Write);
// write the plaintext out to the cryptostream
x_cryptostream.Write(plaintext, 0, plaintext.Length);
// close the CryptoStream
x_cryptostream.Close();
// get the ciphertext from the MemoryStream
byte[] x_ciphertext = x_memory_stream.ToArray();
// close memory stream
x_memory_stream.Close();
// convert from array to string
string cipher_Tx = Encoding.Default.GetString(x_ciphertext,
0, x_ciphertext.Length);
x_encryptor.Dispose();
x_alg.Clear();
byte[] cipher = Encoding.Default.GetBytes(cipher_Tx);
return cipher;
}
Ответ №1:
Ваша проблема в string cipher_Tx = Encoding.Default.GetString(x_ciphertext, 0, x_ciphertext.Length);
.
x_ciphertext
это недопустимое байтовое представление текста, в нем много непрезентабельных символов, и когда вы выполняете преобразование byte[]
в string
, вы теряете информацию. Правильный способ сделать это — использовать строковый формат, предназначенный для представления двоичных данных с использованием чего-то вроде Convert.ToBase64String(byte[])
и Convert.FromBase64String(string)
.
string cipher_Tx = Convert.ToBase64String(x_ciphertext)
x_encryptor.Dispose();
x_alg.Clear();
byte[] cipher = Convert.FromBase64String(cipher_Tx)
При этом в вашем коде есть много других «странных» вещей, например, вы не используете using
операторы, а вам действительно следует. Также все это преобразование в строку и обратно совершенно не нужно, просто верните x_ciphertext
. Могут быть и другие проблемы с кодом (например, откуда взялись строки для Key
и IV
) и многие другие рекомендации (например, вы должны генерировать случайный IV и записывать его в выходные данные, а ключ должен быть сгенерирован с использованием функции вывода ключа не прямо из пользовательского текста), но я прекратил проверку после того, как обнаружил проблему с преобразованием строк.
Комментарии:
1. Спасибо за попытку помочь. Действительно ценю это, я изменил свой код, но это не устраняет исключение, о котором я упоминал. Ключ и IV передаются методами ReadData / StoreData. Этот код написан несколько лет назад. Я считаю, что это как-то связано с блоками, которые использует алгоритм дешифрования данных, не соответствующий размеру или чему-то еще.
2. Ну, вам, похоже, действительно нравится использовать
Encoding.Default.
, и я готов поспорить, что почти каждое его использование неверно. Я бы проверил другие части программы на наличие аналогичных проблем.3. Я просмотрел весь код, связанный с encrypt decrypt, и изменил все строки кодировки. По умолчанию используется Convert.FromBase64String/Convert. ToBase64String. Я все еще получаю недопустимое исключение длины данных для дешифрования.
4. Нет причин для преобразования в базовый 64 и обратно. Посмотрите на byte[] x_ciphertext, а затем на byte[] cipher — они имеют точно такое же значение. Просто верните x_ciphertext — он у вас уже есть.
Ответ №2:
Приведенный выше код работает до тех пор, пока ключ и iv, используемые для дешифрования, совпадают с ключом и iv, используемыми для шифрования. Попробуйте это:
byte[] test = new byte[1000000];
for (int i = 0; i < 256; i )
{
test[i] = (byte)i;
}
var ciphertext = Encrypt(Encoding.Default.GetString(test), "0000000000000000", "0000000000000000");
byte[] check = Decrypt(ciphertext, "0000000000000000", "0000000000000000");
for (int i = 0; i < 256; i )
{
Debug.Assert(check[i] == (byte)i, "round trip");
}
Как вы можете видеть, один миллион байт шифруется и расшифровывается просто отлично с вашим кодом, поэтому я не думаю, что это имеет какое-либо отношение к размеру данных.
Однако измените IV следующим образом:
byte[] check = Decrypt(ciphertext, "0000000000000000", "000000000000000X"); // note X
и отладка.Сработает Assert — расшифровка не будет соответствовать. Однако x_cryptostream.Close() завершается успешно.
Затем попробуйте изменить ключ следующим образом:
byte[] check = Decrypt(ciphertext, "000000000000000X", "0000000000000000"); // note X
Теперь, x_cryptostream.Close() завершится ошибкой с CryptographicException, вероятно, «Заполнение недопустимо и не может быть удалено».
Повреждение ключа приведет к сбою дешифрования и x_cryptostream.Закрыть() для сбоя.
Я думаю, что проблема заключается в вашем сохранении и последующем восстановлении байтов ключа.
Кстати: Надеюсь, вы используете полный двоичный диапазон ключа, а не основываете его только на символах ASCII, иначе у вас действительно нет надежного ключа.
Комментарии:
1. Спасибо. Я случайно удалил заполнение. Я думаю, что изменение кодировки на Convert.FromBase64String(cipher_Tx) и добавление x_alg.Padding = PaddingMode.PKCS7 устранило проблему.