Исключение дешифрования - длина данных для расшифровки недействительна

Я работаю в приложении С#. У нас есть общие методы для хранения данных в файле. Эти методы шифруют данные и сохраняют их в файловой системе. когда нам нужны данные, метод ReadData расшифровывает данные и возвращает мне обычный текст.

Этот код отлично работает в обычных случаях, если размер текста небольшой. но для примера текста, приведенного ниже, код расшифровки выдает исключение - длина данных для расшифровки недействительна.

Исключение возникает в строке

        // close the CryptoStream
        x_cryptostream.Close();

Я пробовал разные способы, но не повезло. Пожалуйста, помогите.

Почему я шифрую уже зашифрованные данные - я просто пытаюсь сохранить их в файл, используя обычный метод огромного приложения. Общие методы storedata(key,data) и 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;
    }  

person Kevin Morocco    schedule 17.03.2014    source источник


Ответы (2)


Ваша проблема 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 и записывать его в вывод, а ключ должен быть сгенерирован используя функцию получения ключа не прямо из пользовательского текста), но я перестал проверять после того, как обнаружил проблему преобразования строки.

person Scott Chamberlain    schedule 17.03.2014
comment
Спасибо за попытку помочь. Искренне ценю это, я изменил свой код, но это не устраняет упомянутое мной исключение. Ключ и IV передаются методами ReadData/StoreData. Этот код написан несколько лет назад. Я считаю, что это как-то связано с блоками алгоритма расшифровки данных, которые не соответствуют размеру или что-то в этом роде. - person Kevin Morocco; 18.03.2014
comment
Что ж, похоже, вам действительно нравится использовать Encoding.Default., и я готов поспорить, что почти каждое его использование неправильно. Я бы проверил другие части программы на наличие подобных проблем. - person Scott Chamberlain; 18.03.2014
comment
Я просмотрел весь код, связанный с шифрованием расшифровки, и изменил все строки Encoding.Default на Convert.FromBase64String/Convert.ToBase64String. Я все еще получаю, что длина данных для расшифровки является недопустимым исключением. - person Kevin Morocco; 18.03.2014
comment
Нет смысла переходить на Base 64 и обратно. Посмотрите на byte[] x_ciphertext, а затем на byte[] cipher — они имеют точно такое же значение. Просто верните x_ciphertext — он у вас уже есть. - person Jim Flood; 19.03.2014

Ваш приведенный выше код работает до тех пор, пока ключ и 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

и Debug.Assert сработает - расшифровка не будет совпадать. Однако x_cryptostream.Close() завершается успешно.

Затем попробуйте изменить ключ следующим образом:

byte[] check = Decrypt(ciphertext, "000000000000000X", "0000000000000000"); // note X

Теперь x_cryptostream.Close() завершится ошибкой CryptographicException, возможно, «заполнение недопустимо и не может быть удалено».

Повреждение ключа приведет к сбою расшифровки и сбою x_cryptostream.Close().

Я думаю, что проблема в вашем сохранении и последующем восстановлении ключевых байтов.

Кстати: надеюсь, вы используете полный двоичный диапазон ключа, а не основываете его только на символах ASCII, иначе у вас действительно нет сильного ключа.

person Jim Flood    schedule 18.03.2014
comment
Спасибо. Я случайно удалил прокладку. Я думаю, что изменение кодировки на Convert.FromBase64String(cipher_Tx) и добавление x_alg.Padding = PaddingMode.PKCS7 устранило проблему. - person Kevin Morocco; 19.03.2014