RijndaelManaged Padding недействителен и не может быть удален, что происходит только при расшифровке в рабочей среде.

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

Приведенный ниже класс обрабатывает шифрование и дешифрование строк, передаваемый ключ и вектор ВСЕГДА одинаковы.

Шифрованные и дешифруемые строки всегда являются числами, большинство из них работают, но иногда происходит сбой при расшифровке (но только на рабочем сервере). Я должен упомянуть, что как локальная, так и производственная среды находятся в IIS6 на Windows Server 2003, код, который использует класс, находится в обработчике .ashx. Пример сбоя на рабочем сервере: «0000232668».

Сообщение об ошибке

System.Security.Cryptography.CryptographicException: заполнение недействительно и не может быть удалено. в System.Security.Cryptography.RijndaelManagedTransform.DecryptData (Byte [] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte [] & outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast)

А для кода

 public class Aes
    {
        private byte[] Key;
        private byte[] Vector;

        private ICryptoTransform EncryptorTransform, DecryptorTransform;
        private System.Text.UTF8Encoding UTFEncoder;

        public Aes(byte[] key, byte[] vector)
        {
            this.Key = key;
            this.Vector = vector;

            // our encyption method
            RijndaelManaged rm = new RijndaelManaged();

            rm.Padding = PaddingMode.PKCS7;

            // create an encryptor and decyptor using encryption method. key and vector
            EncryptorTransform = rm.CreateEncryptor(this.Key, this.Vector);
            DecryptorTransform = rm.CreateDecryptor(this.Key, this.Vector);

            // used to translate bytes to text and vice versa
            UTFEncoder = new System.Text.UTF8Encoding();
        }

        /// Encrypt some text and return a string suitable for passing in a URL. 
        public string EncryptToString(string TextValue)
        {
            return ByteArrToString(Encrypt(TextValue));
        }

        /// Encrypt some text and return an encrypted byte array. 
        public byte[] Encrypt(string TextValue)
        {
            //Translates our text value into a byte array. 
            Byte[] bytes = UTFEncoder.GetBytes(TextValue);
            Byte[] encrypted = null;

            //Used to stream the data in and out of the CryptoStream. 
            using (MemoryStream memoryStream = new MemoryStream())
            {                
                using (CryptoStream cs = new CryptoStream(memoryStream, EncryptorTransform, CryptoStreamMode.Write))
                {
                    cs.Write(bytes, 0, bytes.Length);                    
                }

                encrypted = memoryStream.ToArray();                
            }

            return encrypted;
        }

        /// The other side: Decryption methods 
        public string DecryptString(string EncryptedString)
        {
            return Decrypt(StrToByteArray(EncryptedString));
        }

        /// Decryption when working with byte arrays.     
        public string Decrypt(byte[] EncryptedValue)
        {
            Byte[] decryptedBytes = null;

            using (MemoryStream encryptedStream = new MemoryStream())
            {
                using (CryptoStream decryptStream = new CryptoStream(encryptedStream, DecryptorTransform, CryptoStreamMode.Write))
                {
                    decryptStream.Write(EncryptedValue, 0, EncryptedValue.Length);
                }

                decryptedBytes = encryptedStream.ToArray();
            }

            return UTFEncoder.GetString(decryptedBytes);
        }

        /// Convert a string to a byte array.  NOTE: Normally we'd create a Byte Array from a string using an ASCII encoding (like so). 
        //      System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); 
        //      return encoding.GetBytes(str); 
        // However, this results in character values that cannot be passed in a URL.  So, instead, I just 
        // lay out all of the byte values in a long string of numbers (three per - must pad numbers less than 100). 
        public byte[] StrToByteArray(string str)
        {
            if (str.Length == 0)
                throw new Exception("Invalid string value in StrToByteArray");

            byte val;
            byte[] byteArr = new byte[str.Length / 3];
            int i = 0;
            int j = 0;
            do
            {
                val = byte.Parse(str.Substring(i, 3));
                byteArr[j++] = val;
                i += 3;
            }
            while (i < str.Length);
            return byteArr;
        }

        // Same comment as above.  Normally the conversion would use an ASCII encoding in the other direction: 
        //      System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); 
        //      return enc.GetString(byteArr);     
        public string ByteArrToString(byte[] byteArr)
        {
            byte val;
            string tempStr = "";
            for (int i = 0; i <= byteArr.GetUpperBound(0); i++)
            {
                val = byteArr[i];
                if (val < (byte)10)
                    tempStr += "00" + val.ToString();
                else if (val < (byte)100)
                    tempStr += "0" + val.ToString();
                else
                    tempStr += val.ToString();
            }
            return tempStr;
        }

РЕДАКТИРОВАТЬ: Спасибо за вашу помощь, однако ваши ответы не раскрыли проблему, которая оказалась глупо простой. Я генерировал зашифрованную строку на одном сервере и передавал ее обработчику на другом сервере для дешифрования и обработки, но оказалось, что результаты шифрования различаются при запуске на разных серверах, поэтому принимающий сервер не мог ее расшифровать. Один из ответов наткнулся на намек на это случайно, поэтому я его принял.


person Nick Allen    schedule 22.01.2010    source источник


Ответы (3)


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

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

person David M    schedule 22.01.2010
comment
Эээ, извините за комментарии, это заимствованный код из другого вопроса SO. Откуда взялось Строка или код? - person Nick Allen; 22.01.2010
comment
Также, почему это будет работать в локальной среде, если это так? Не сомневаюсь в твоем ответе, это кажется странным - person Nick Allen; 22.01.2010
comment
Строки - это коды для изображений, поступающих из библиотеки изображений. - person Nick Allen; 22.01.2010
comment
Хорошо, но какая кодировка для них? Это зашифрованная строка из приведенного выше кода, которую вы затем пытаетесь расшифровать с помощью приведенного выше кода? Просто нужно немного больше контекста. - person David M; 22.01.2010
comment
Нет, строка - это исходная строка идентификатора изображения, которую я получаю из веб-службы. Затем я шифрую строку, используя метод encrypttostring, описанный выше, который превращает ее в 105162040231029115202011159160207173133005046245. Когда я затем расшифровываю длинное число, я получаю ошибку заполнения. Спасибо, что посмотрели на это BTW - person Nick Allen; 22.01.2010
comment
Причина, по которой мне нужен формат длинных чисел, заключается в том, что мне нужно передать зашифрованный идентификатор изображения в строке запроса - person Nick Allen; 22.01.2010
comment
И дает ли EncryptToString с одинаковыми входными данными одинаковые выходные данные как в производственной среде, так и в среде разработки? Потому что у него определенно нет допустимого заполнения PKCS7, как оно есть ... - person David M; 22.01.2010
comment
Только что провел тест, и нет! это дает мне. 112012252134066051251251177029075026100201077033 - person Nick Allen; 22.01.2010
comment
На самом деле мотив моего вопроса был неправильным, поэтому, пожалуйста, проигнорируйте мой последний комментарий, но, похоже, это был правильный вопрос! - person David M; 22.01.2010
comment
РЖУ НЕ МОГУ. Хорошо, будучи новичком в шифровании, почему это происходит? - person Nick Allen; 22.01.2010
comment
Обновление здесь некорректно, просто неправда, что результаты шифрования различаются при запуске на разных серверах. Суть симметричного шифрования заключается в том, что при одинаковой поставке исходного текста, ключа и IV алгоритмы шифрования / дешифрования дают одинаковый результат. Значения по умолчанию могут отличаться в зависимости от различных конфигураций .NET - проверьте свойство Padding вашего экземпляра RijndaelManaged на обоих серверах. Также убедитесь, что все ваши кодировки совпадают, чтобы вы не теряли данные при передаче зашифрованной и закодированной строки. - person Nate Cook; 21.04.2012
comment
Нейт - это комментарий к вопросу ОП, а не мой ответ на него. Думаю, вы положили его не туда ... - person David M; 21.04.2012
comment
Есть ли разница в шифровании чего-либо в системе x64 и расшифровке в системе x86 (с тем же кодом)? - person juFo; 14.02.2013
comment
У меня была аналогичная проблема ... разница, в конце концов, заключалась в том, как я передавал данные (строку) для шифрования. Если я скопировал / вставил в текстовое поле, шифрование было другим, чем при жестком кодировании в программе. Короче говоря ... кодировка исходных данных имеет большое значение! - person Paul; 18.03.2013

Я обычно называю FlushFinalBlock явно на CryptoStream перед его закрытием. Это означало бы, что в вашем методе шифрования нужно сделать следующее:

using (CryptoStream cs = new CryptoStream(memoryStream, EncryptorTransform, CryptoStreamMode.Write))
{
    cs.Write(bytes, 0, bytes.Length);
    cs.FlushFinalBlock();        
}

Если вы этого не сделаете, возможно, зашифрованные данные усекаются - это приведет к сценарию «недопустимого заполнения». Заполнение всегда присутствует при использовании PKCS7, даже если шифруемые данные выровнены по длине блока шифра.

person Dave Cluderay    schedule 22.01.2010
comment
FlushFinalBlock () вызывается при удалении CryptoStream, что происходит автоматически с помощью оператора using. - person Nick Allen; 22.01.2010
comment
Может быть, они исправили этот недостаток, но однажды CryptoStream.Dispose НЕ вызывал FlushFinalBlock! bytes.com/topic/c-sharp/ ответы / - person Dave Cluderay; 22.01.2010
comment
Похоже, они исправили это в .NET 2.0, но я определенно предлагаю выработать привычку самостоятельно сбрасывать последний блок. - person Dave Cluderay; 22.01.2010
comment
Ну, как-то я добавил оператор FlushFinalBlock, и это помогло мне избавиться от ошибки недопустимого заполнения при расшифровке. - person cdpnet; 26.02.2012
comment
Явное добавление FlushFinalBlock в дополнение к блокам using решило мои проблемы и в .NET 4.5 ... - person CameraSchoolDropout; 14.05.2013

это приводит к символьным значениям, которые нельзя передать в URL

Есть ли причина, по которой вы используете свою собственную кодировку StrToByteArray вместо Кодировка Base64?

Если вы внесете эти изменения:

public string EncryptToString(string TextValue)
{
  return Convert.ToBase64String(Encrypt(TextValue));
}

public string DecryptToString(string TextValue)
{
  return Decrypt(Convert.FromBase64String(TextValue));
}

тогда все должно работать намного лучше.

Изменить:
Что касается проблемы с ToBase64String и QueryString:
Если вы выполняете свой собственный синтаксический анализ QueryString, вам необходимо убедиться, что вы выполняете разделение только по первому знаку =.

var myURL = "http://somewhere.com/default.aspx?encryptedID=s9W/h7Sls98sqw==&someKey=someValue";
var myQS = myURL.SubString(myURL.IndexOf("?") + 1);
var myKVPs = myQS.Split("&");
foreach (var kvp in myKVPs) {
  // It is important you specify a maximum number of 2 elements
  // since the Base64 encoded string might contain =-signs.
  var keyValue = kvp.Split("=", 2);
  var key = keyValue[0];
  var value = keyValue[1];
  if (key == "encryptedID")
    var decryptedID = myAES.DecryptToString(value);
}

Таким образом, вам не нужно заменять какие-либо символы в QueryString, когда он закодирован в Base64.

person Sani Singh Huttunen    schedule 22.01.2010
comment
Это потому, что мне нужно передать зашифрованную строку в качестве параметра строки запроса. Я использовал ToBase64String и заменял строку на символах /, = и другой, который ускользает от меня в данный момент, формат длинных чисел казался более простым - person Nick Allen; 22.01.2010
comment
При использовании результата в строке запроса делать замену не нужно. Я много лет использую ToBase64String без проблем со строками запроса. - person Sani Singh Huttunen; 22.01.2010
comment
Умм, я не понимаю, как можно передавать символы и =, + или / в значениях строки запроса и не нарушать URL - person Nick Allen; 22.01.2010
comment
Парсеры URL и QueryString умны. Они работают аналогично приведенному выше примеру. Если после? тогда он рассматривается как часть QueryString. В строке KeyValuePair может быть только один знак = - первый. Остальные считаются частью стоимости. - person Sani Singh Huttunen; 22.01.2010