Почему в моем расшифрованном тексте появляются случайные символы?

вступление

Я пытаюсь шифровать и расшифровывать тексты, и иногда, особенно для больших текстов, в расшифрованном тексте появляются случайные символы. Я использую криптографию AES в пространстве имен System.Security.Cryptography, и текст, который я пытаюсь зашифровать в данный момент, будет URL-адресом и некоторой информацией, такой как заголовок страницы. Я привел пример ниже и то, что я пытался. Я также написал два метода шифрования и дешифрования, за исключением строк, выводящихся в окно отладки. Используемые ключ и IV не должны быть проблемой, так как на данный момент они будут постоянными.

Думаю, было бы разумно указать, что он шифрует и расшифровывает 18/01/2013;18/01/2013 в отдельном случае, как и ожидалось.

Пример

Скажем, я хотел расшифровать этот текст:

Barnabe Googes Information & Homepage | Search and Research on BarnabeGooge.com;18/01/2013;18/01/2013;;http://www.googe.com

По умолчанию он использует UTF-8 и будет шифровать:

뤟౏羜ڮ胂淺弊놛荧ꠃ錺槝ヸ蘜ầᄼꕒヘ⍩㗪潺뱂施㒞ꨛ殳硪픴ی뿨춃�燲ᯁﱪ뙊힓琲鯖嶑⨹갂Ѭ쳀鿜�྄䋖⭫ퟂ㪏�荾ꆺשּ붹梾麦膛

И расшифровывает обратно:

Barnabe Googes Information & Homepage | Search and Research on B���Ax2�!��f�M]18/01/20�;18/01[�;>َ�l?����m��*-��+��^A[=�

Что я пытался

  • Я пытался перейти на другие кодировки, но UTF-8, похоже, меньше всего влияет на расшифрованный текст.
  • Изменен на разные типы отступов, но Padding.Zeros кажется лучшим. Я также не могу использовать Padding.None, потому что он выдает NotSupportedException: bad data length.
  • Изменил Mode на CBC (не то чтобы это имело значение).
  • Сбросить/закрыть CryptoStream, чтобы он мог сбросить последний блок или что-то в этом роде.
  • На всякий случай ошибка связана с заголовком, я использовал WebUtility.HtmlDecode() для декодирования заголовка, но это не повлияло на него.

Метод шифрования

Как видите, в приведенном ниже шифровании используется шифрование AES. Я хочу отметить, что key и IV — это две глобальные строки в том же классе, что и методы шифрования и дешифрования. Причина, по которой я сделал это, состоит в том, чтобы возиться с разными кодировками и CryptographyServiceProviders, просто если случайно случайное изменение сработает. Пожалуйста, игнорируйте их, так как они постоянны и не повлияют на окончательное шифрование/дешифрование.

public static byte[] EncryptStringToBytes(string plainText, Encoding Enc)
{

    if (plainText == null || plainText.Length <= 0)
            throw new ArgumentNullException("plainText");
    byte[] encrypted;
    using (AesCryptoServiceProvider tdsAlg = new AesCryptoServiceProvider())
    {
         tdsAlg.Key = (byte[])Enc.GetBytes(key).Take(tdsAlg.Key.Length).ToArray();
         tdsAlg.IV = (byte[])Enc.GetBytes(IV).Take(tdsAlg.IV.Length).ToArray();
         tdsAlg.Padding = PaddingMode.Zeros;
         tdsAlg.Mode = CipherMode.CBC;
         ICryptoTransform encryptor = tdsAlg.CreateEncryptor(tdsAlg.Key, tdsAlg.IV);

         using (MemoryStream msEncrypt = new MemoryStream())
         {
             using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
             {
                 using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                 {
                     swEncrypt.Write(plainText);
                 }
                 encrypted = msEncrypt.ToArray();
                 csEncrypt.Close();
             }
         }
    }

    return encrypted;
}

Метод расшифровки

public static string DecryptStringFromBytes(byte[] cipherText,Encoding Enc)
{
    if (cipherText == null || cipherText.Length <= 0)
        throw new ArgumentNullException("cipherText");

    string plaintext = null;

    using (AesCryptoServiceProvider tdsAlg = new AesCryptoServiceProvider())
    {
        tdsAlg.Key = (byte[])Enc.GetBytes(key).Take(tdsAlg.Key.Length).ToArray();
        tdsAlg.IV = (byte[])Enc.GetBytes(IV).Take(tdsAlg.IV.Length).ToArray() ;
        tdsAlg.Padding = PaddingMode.Zeros;
        tdsAlg.Mode = CipherMode.CBC;
        ICryptoTransform decryptor = tdsAlg.CreateDecryptor();

        using (MemoryStream msDecrypt = new MemoryStream(cipherText))
        {
            using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
            {
                using (StreamReader srDecrypt = new StreamReader(csDecrypt,true))
                {
                    plaintext = srDecrypt.ReadToEnd().Replace("\0","");
                    csDecrypt.Close();
                    return plaintext.Replace("\0",string.Empty);
                }
            }
        }
   }    
   return plaintext;    
}

Загрузочная заметка

На всякий случай я использую это, чтобы получить заголовок веб-страницы, но, как я уже упоминал, использование HtmlDecode не влияет на это.

WebClient x = new WebClient();
string source = x.DownloadString(Url);
x.Dispose();
string title= Regex.Match(source, @"\<title\b[^>]*\>\s*(?<Title>[\s\S]*?)\</title\>", RegexOptions.IgnoreCase).Groups["Title"].Value;
title = title.Replace(";", " ");
return title;

person Brownish Monster    schedule 18.01.2013    source источник
comment
(слишком долго, не читал код) :) - исходя из того, что вы получили часть контента, расшифрованного нормально, скорее всего, какая-то строка-›байт-›кода преобразования неверна.   -  person Alexei Levenkov    schedule 18.01.2013
comment
взгляните на этот сайт MSDN, возможно, вы делаете что-то не так, слишком много кода в вашем вопросе, чтобы кто-то мог прочитать Класс AesCryptoServiceProvider   -  person MethodMan    schedule 18.01.2013
comment
Вы не можете сохранить byte[], полученный из шифрования, в строку или текстовый файл. Нормализация строк уничтожит значения байтов. Вместо этого используйте FileStream. Используйте кодировку base64, если вам действительно нужна строка.   -  person Hans Passant    schedule 18.01.2013
comment
Я вижу много тонких и не очень проблем с этой криптовалютой. Я настоятельно рекомендую вам повторно использовать код, написанный экспертами, иначе вы получите испорченную безопасность, даже не подозревая об этом. Это будет выглядеть безопасным, но это не так.   -  person usr    schedule 18.01.2013
comment
Спасибо, Ханс. Я использовал Encoding.Utf8.GetString() или GetBytes(), я никогда не знал о base64.   -  person Brownish Monster    schedule 18.01.2013
comment
+1 Теперь это вопрос, который стоит назвать «вопросом» на SO.   -  person jAC    schedule 18.01.2013
comment
Восстановите свой ответ, объясняющий решение, и примите его или полностью удалите вопрос. Включение [Решено] в заголовок - это не то, как работает SO. Рад, что вы нашли решение, хотя!   -  person erickson    schedule 18.01.2013
comment
Я не могу принять это, мне нужно подождать 2 дня, поэтому я отредактировал исходный пост и поместил решение внизу. Но теперь я удалил ответ и удалил ответ в исходном сообщении.   -  person Brownish Monster    schedule 18.01.2013


Ответы (2)


Благодаря Хансу Пассанту я нашел решение. Проблема заключалась в том, что я использовал Encoding.GetString() или Encoding.GetBytes() при шифровании и расшифровке, тогда как мне следовало использовать Convert.ToBase64String() или Convert.FromBase64String().

person Brownish Monster    schedule 18.01.2013
comment
Зачем нам нужен base64 для шифрования и дешифрования? Вы читаете данные из файлов? и запись данных в файлы? - person shuva; 10.01.2018
comment
@shuva Я писал и читал данные в файлы, но сначала хотел их зашифровать. Это был любимый проект некоторое время назад, поэтому я не могу вспомнить подробности. - person Brownish Monster; 11.01.2018
comment
В моем случае у меня была проблема, что открытие файла в двоичном режиме добавляет в файл дополнительные символы. Например, fopen(filename, w) вызывает дополнительные символы. И не удалось расшифровать. - person shuva; 11.01.2018

У меня была такая же проблема с дополнительным выходом. Для меня это не было проблемой кодирования, потому что я передал его как массив байтов в библиотеке BCrypt. Поскольку это обычный текст, я бы использовал пробел в качестве дополнения перед шифрованием и обрезал после расшифровки.

int padding = BLOCK_SIZE - (input_len+1)%BLOCK_SIZE;
if(padding && (input_len+padding) <= buf_size)
{
   memset(buf+input_len, ' ', padding);
   input_len += padding;
}

Для 128 битного шифрования размер блока равен 16. Обратите внимание, что buf_size должно быть кратно block-size, чтобы он работал постоянно. Поскольку мы уже дополнили ввод, нам не нужен алгоритм заполнения при расшифровке.

tdsAlg.Padding = PaddingMode.None;

И в конце расшифровки я бы обрезал вывод.

person shuva    schedule 10.01.2018
comment
Нет, проблема в том, что byte[] он вышел из шифрования, по которому он вызывал Encoding.Utf8.GetString(, а затем по расшифровке он вызывал Encoding.Utf8.GetBytes(. GetString не может обработать byte[], который изначально не был строкой, значения для недопустимого UTF-8 во входном массиве байтов вызовут ошибки в процессе преобразования. Вам нужно закодировать строку, используя метод, предназначенный для хранения произвольных массивов байтов, одним из которых является Base64. - person Scott Chamberlain; 10.01.2018
comment
Так нельзя ли отправлять байты в формате UTF-8? - person shuva; 11.01.2018
comment
Не произвольные двоичные байты это не так. Именно для этого Base64 предназначен для представления произвольных байтов в виде текста, но, поскольку он должен работать со всеми байтами и не может сломаться, как любой из основанных на кодировании, стоимость строки будет на 4/3 больше, чем исходный байт[] - person Scott Chamberlain; 11.01.2018