RijndaelManaged «Заполнение недопустимо и не может быть удалено», которое происходит только при расшифровке в производстве - PullRequest
15 голосов
/ 22 января 2010

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

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

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

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

Ответы [ 3 ]

12 голосов
/ 22 января 2010

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

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

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

12 голосов
/ 22 января 2010

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

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

2 голосов
/ 22 января 2010

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

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

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

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

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

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

Edit:
Относительно проблемы с ToBase64String и QueryString:
Если вы выполняете собственный анализ QueryString, вам нужно убедиться, что вы используете только Split для первого = -sign.

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.

...