Сведения о вопросе

Life

10:32, 7th August, 2020

Теги

c#   .net   exception   encryption    

Почему плохой пароль вызывает "заполнение недопустимо и не может быть удалено"?

Просмотров: 652   Ответов: 9

Мне нужно было какое-то простое строковое шифрование, поэтому я написал следующий код (с большим количеством "inspiration" отсюда ):

    // create and initialize a crypto algorithm
    private static SymmetricAlgorithm getAlgorithm(string password) {
        SymmetricAlgorithm algorithm = Rijndael.Create();
        Rfc2898DeriveBytes rdb = new Rfc2898DeriveBytes(
            password, new byte[] {
            0x53,0x6f,0x64,0x69,0x75,0x6d,0x20,             // salty goodness
            0x43,0x68,0x6c,0x6f,0x72,0x69,0x64,0x65
        }
        );
        algorithm.Padding = PaddingMode.ISO10126;
        algorithm.Key = rdb.GetBytes(32);
        algorithm.IV = rdb.GetBytes(16);
        return algorithm;
    }

    /* 
     * encryptString
     * provides simple encryption of a string, with a given password
     */
    public static string encryptString(string clearText, string password) {
        SymmetricAlgorithm algorithm = getAlgorithm(password);
        byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, algorithm.CreateEncryptor(), CryptoStreamMode.Write);
        cs.Write(clearBytes, 0, clearBytes.Length);
        cs.Close();
        return Convert.ToBase64String(ms.ToArray());
    }

    /*
     * decryptString
     * provides simple decryption of a string, with a given password
     */
    public static string decryptString(string cipherText, string password) {
        SymmetricAlgorithm algorithm = getAlgorithm(password);
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, algorithm.CreateDecryptor(), CryptoStreamMode.Write);
        cs.Write(cipherBytes, 0, cipherBytes.Length);
        cs.Close();            
        return System.Text.Encoding.Unicode.GetString(ms.ToArray());
    }

Код, похоже, работает нормально, за исключением того, что при расшифровке данных с неверным ключом я получаю CryptographicException - "Padding is invalid and cannot be removed" - на строке cs.Close() в decryptString.

пример кода:

    string password1 = "password";
    string password2 = "letmein";
    string startClearText = "The quick brown fox jumps over the lazy dog";
    string cipherText = encryptString(startClearText, password1);
    string endClearText = decryptString(cipherText, password2);     // exception thrown

Мой вопрос в том, можно ли этого ожидать? Я бы подумал, что расшифровка с неверным паролем просто приведет к бессмысленному выходу, а не к исключению.



  Сведения об ответе

+-*/

06:03, 8th August, 2020

Хотя на этот вопрос уже был дан ответ, я думаю, что было бы неплохо объяснить, почему этого следует ожидать.

Обычно применяется схема заполнения, поскольку большинство криптографических фильтров не являются семантически безопасными и предотвращают некоторые формы криптотаксов. Например, обычно в RSA используется схема заполнения OAEP , которая предотвращает некоторые виды атак (такие как выбранная атака открытого текста или ослепление ).

Схема заполнения добавляет некоторый (обычно) случайный мусор к сообщению m перед отправкой сообщения. Например, в методе OAEP используются два Оракула (это упрощенное объяснение):

  1. Учитывая размер модуля, вы набираете K1 бит с 0 и K0 бит со случайным числом.
  2. Затем, применяя некоторое преобразование к сообщению, вы получаете дополненное сообщение, которое шифруется и отправляется.

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


  Сведения об ответе

JUST___

07:45, 21st August, 2020

Я испытал подобное исключение "Padding is invalid and cannot be removed.", но в моем случае ключ IV и заполнение были правильными.

Оказалось, что промывка криптопотока - это все, чего не хватало.

Подобный этому:

            MemoryStream msr3 = new MemoryStream();
            CryptoStream encStream = new CryptoStream(msr3, RijndaelAlg.CreateEncryptor(), CryptoStreamMode.Write);
            encStream.Write(bar2, 0, bar2.Length);
            // unless we flush the stream we would get "Padding is invalid and cannot be removed." exception when decoding
            encStream.FlushFinalBlock();
            byte[] bar3 = msr3.ToArray();


  Сведения об ответе

ITSME

05:42, 12th August, 2020

Если вы хотите, чтобы ваше использование было правильным, Вы должны добавить аутентификацию к вашему шифртексту, чтобы вы могли проверить, что это правильный пароль или что шифртекст не был изменен. Заполнение, которое вы используете ISO10126 , вызовет исключение только в том случае, если последний байт не расшифровывается как одно из 16 допустимых значений для заполнения (0x01-0x10). Таким образом, у вас есть 1/16 шанс того, что он NOT выбрасывает исключение с неверным паролем, где, если вы аутентифицируете его, у вас есть детерминированный способ определить, является ли ваша расшифровка действительной.

Использование crypto api хотя и кажется простым, на самом деле довольно легко совершать ошибки. Например, вы используете фиксированную соль для вашего ключа и IV деривации, это означает, что каждый зашифрованный шифр с одним и тем же паролем будет повторно использовать его IV с этим ключом, что нарушает семантическую безопасность в режиме CBC, IV должен быть одновременно непредсказуемым и уникальным для данного ключа.

По этой причине легко ошибиться, у меня есть фрагмент кода, который я стараюсь держать пересмотренным и актуальным (комментарии, вопросы приветствуются):

Современные примеры симметричного аутентифицированного шифрования строки C#.

Если вы используете его AESThenHMAC.AesSimpleDecryptWithPassword(ciphertext, password) , когда используется неправильный пароль, null возвращается, если шифртекст или iv был изменен после шифрования null возвращается, вы никогда не получите ненужные данные или исключение заполнения.


  Сведения об ответе

crush

12:55, 29th August, 2020

Если вы исключили ключевое несоответствие, то, кроме FlushFinalBlock() (см. ответ Янива), достаточно также вызвать Close() на CryptoStream .

Если вы очищаете ресурсы строго с блоками using , обязательно вложите блок для самого CryptoStream :

using (MemoryStream ms = new MemoryStream())
using (var enc = RijndaelAlg.CreateEncryptor())
{
  using (CryptoStream encStream = new CryptoStream(ms, enc, CryptoStreamMode.Write))
  {
    encStream.Write(bar2, 0, bar2.Length);
  } // implicit close
  byte[] encArray = ms.ToArray();
}

Я был укушен этим (или подобным):

using (MemoryStream ms = new MemoryStream())
using (var enc = RijndaelAlg.CreateEncryptor())
using (CryptoStream encStream = new CryptoStream(ms, enc, CryptoStreamMode.Write))
{
  encStream.Write(bar2, 0, bar2.Length);
  byte[] encArray = ms.ToArray();
} // implicit close -- too late!


  Сведения об ответе

LAST

16:45, 1st August, 2020

Да, этого следует ожидать, или, по крайней мере, именно это происходит, когда наши криптопрограммы получают не поддающиеся расшифровке данные


  Сведения об ответе

SSESION

07:38, 24th August, 2020

Другой причиной исключения может быть состояние гонки между несколькими потоками, использующими логику расшифровки-собственные реализации ICryptoTransform не являются потокобезопасными (например, SymmetricAlgorithm), поэтому его следует поместить в раздел exclusive, например, используя блокировку . Пожалуйста, обратитесь сюда для получения более подробной информации: http://www.make-awesome.com/2011/07/system-security-cryptography-and-thread-safety/


  Сведения об ответе

repe

21:34, 29th August, 2020

Там может быть несколько непрочитанных байт в CryptoStream. Закрытие перед чтением потока полностью вызывало ошибку в моей программе.


  Сведения об ответе

dump

03:20, 23rd August, 2020

У меня была аналогичная проблема, проблема в методе decrypt заключалась в инициализации пустого потока памяти. когда он работал, когда я инициализировал его с помощью шифровального текстового байтового массива, как это:

MemoryStream ms = new MemoryStream(cipherText)


  Сведения об ответе

screen

17:28, 20th August, 2020

Ответ, обновленный пользователем "atconway", сработал для меня.

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться