2010-01-22 112 views
15

我知道其他問題已被提出,但迄今爲止都沒有提供解決方案,或者確實是我的問題。RijndaelManaged「填充無效且無法刪除」僅在生產中解密時纔會發生

下面的類處理字符串的加密和解密,傳入的鍵和向量始終是相同的。

被加密和解密的字符串總是數字,大部分工作,但偶爾在解密時(但只在生產服務器上)失敗。我應該提到,本地和生產環境都在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,布爾式Flash)

而對於代碼

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; 
     } 

編輯:謝謝你的所有幫助,但你的答案並沒有解決問題,結果是愚蠢的簡單。我在一臺服務器上生成一個加密的字符串,並將其交給另一臺服務器上的一個處理程序進行解密和處理,但事實證明,在不同服務器上運行加密的結果不同,因此接收服務器無法解密它。其中一個答案絆倒了這個意外,這就是爲什麼我接受它

回答

11

你有時會得到一個關於無效填充的信息,當加密和解密出於任何原因沒有使用相同的密鑰或初始化向量。填充是將多個字節添加到您的明文末尾,以使其達到密碼所需的全部塊數。在PKCS7填充中,每個字節等於添加的字節數,所以在解密後總是可以刪除。解密導致最後一個字節的最後一個字節不等於值n(希望句子有意義)。所以我會仔細檢查你的所有鑰匙。

另外,在你的情況下,我會建議確保你爲每個加密和解密操作創建並配置一個RijndaelManagedTransform實例,並用鍵和向量初始化它。這個問題很可能是由於重用這個變換對象造成的,這意味着第一次使用後,它不再處於正確的初始狀態。

+0

對於這些意見,感到抱歉,這是來自其他問題的「借來的代碼」。 「 」它來自哪裏「 字符串或代碼? – 2010-01-22 10:43:26

+0

字符串。很高興它是借來的代碼,我不必小心翼翼... – 2010-01-22 10:44:14

+0

此外,爲什麼它會在當地環境中工作,如果是這樣的話?不質疑你的答案,看起來很奇怪 – 2010-01-22 10:44:34

2

這導致字符值不能在URL

傳遞有沒有理由你正在使用的不是Base64編碼自己的編碼,StrToByteArray,?

如果你做這些改變:

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

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

那麼事情應該工作好了很多。

編輯:
關於問題ToBase64String和查詢字符串:
如果你做你自己的查詢字符串解析,那麼你需要確保你只拆分第一個= - 符號。

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); 
} 

這樣,當Base64編碼時,您不需要替換QueryString中的任何字符。

+0

它是因爲我需要傳遞加密字符串作爲查詢字符串參數。我正在使用ToBase64String並對字符「/」,「=」進行字符串替換,另一個字符在此刻轉義了我,長數字格式似乎更簡單 – 2010-01-22 10:54:06

+0

使用結果時不需要做任何替換在查詢字符串中。我一直在使用ToBase64String很多年,在querystrings中沒有任何問題。 – 2010-01-22 10:56:00

+0

嗯,我不明白你怎麼可以傳遞和查詢字符串值中的=,+或/字符並且不會破壞url – 2010-01-22 11:20:38

12

我傾向於關閉之前顯式調用的CryptoStream的FlushFinalBlock方法。這將意味着在做你的加密方法如下:

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

如果你不這樣做,它可能是加密的數據被截斷 - 這將導致「無效的填充」的情景。即使正在加密的數據與密碼的塊長度對齊,使用PKCS7時填充始終存在。

+1

當處置CryptoStream時會調用FlushFinalBlock(),它會自動使用using語句 – 2010-01-22 12:35:41

+1

也許他們修復了這個漏洞,但它確實曾經是CryptoStream.Dispose未調用FlushFinalBlock的情況! http://bytes.com/topic/c-sharp/answers/255847-encrypt-string-string-vice-versa – 2010-01-22 13:47:37

+0

看起來像他們在.NET 2.0中修復它,但我肯定會建議養成沖洗的習慣最後阻止你自己。 – 2010-01-22 13:55:05

相關問題