Dot-Net

RijndaelManaged“填充無效且無法刪除”,僅在生產中解密時出現

  • December 4, 2012

我知道有人對此提出了其他問題,但到目前為止還沒有提供解決方案或正是我遇到的問題。

下面的類處理字元串的加密和解密,傳入的密鑰和向量總是相同的。

被加密和解密的字元串總是數字,大多數工作但偶爾會在解密時失敗(但僅在生產伺服器上)。我應該提到,本地和生產環境都在 Windows Server 2003 上的 IIS6 中,使用該類的程式碼位於 .ashx 處理程序中。在生產伺服器上失敗的例子是“0000232668”

錯誤資訊是

System.Security.Cryptography.CryptographicException:填充無效且無法刪除。在 System.Security.Cryptography.RijndaelManagedTransform.DecryptData(字節

$$ $$inputBuffer, Int32 inputOffset, Int32 inputCount, 字節$$ $$& 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;
       }

**編輯:**感謝您的所有幫助,但是您的答案並沒有解決問題,結果證明這是非常簡單的事情。我在一台伺服器上生成了一個加密字元串,並將其交給另一台伺服器上的處理程序進行解密和處理,但事實證明,在不同伺服器上執行時加密的結果不同,因此接收伺服器無法解密它。其中一個答案偶然發現了這個提示,這就是我接受它的原因

當加密和解密由於某種原因沒有使用相同的密鑰或初始化向量時,您有時會收到有關無效填充的消息。填充是添加到明文末尾的一些字節,以使其組成完整數量的塊以供密碼處理。在 PKCS7 填充中,每個字節都等於添加的字節數,因此在解密後始終可以將其刪除。您的解密導致最後n個字節不等於最後一個字節的值n的字元串(希望這句話有意義)。所以我會仔細檢查你所有的鑰匙。

或者,在您的情況下,我建議您確保RijndaelManagedTransform為每個加密和解密操作創建和處置一個實例,並使用密鑰和向量對其進行初始化。這個問題很可能是重用這個變換對象造成的,這意味著第一次使用後,它不再處於正確的初始狀態。

我傾向於在關閉 CryptoStream 之前顯式呼叫FlushFinalBlock方法。這意味著在您的加密方法中執行以下操作:

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

如果您不這樣做,則可能是加密數據被截斷 - 這將導致“無效填充”情況。使用 PKCS7 時始終存在填充,即使要加密的數據與密碼的塊長度對齊。

引用自:https://stackoverflow.com/questions/2116607