2011-02-07 93 views
4

你好,我想通過Rijaendal加密/解密一個字符串。 我根本無法弄清楚爲什麼解密爆炸。我總是以不正確的填充錯誤結束。拋出我的一件事就是我作爲HEX數組返回的加密結果。它有14個字節的長度。在我的解密函數中,相同的字節數組在從HEX轉換後最終有16個字節。Rijndael填充錯誤

任何幫助,將理解:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace rjandal 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string DataForEncrypting = "this is a test"; 

      string key = string.Empty; 
      string iv = string.Empty; 

      using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged()) 
      { 
       rmt.KeySize = 256; 
       rmt.BlockSize = 128; 
       rmt.Mode = System.Security.Cryptography.CipherMode.CBC; 
       rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126; 
       rmt.GenerateKey(); 
       rmt.GenerateIV(); 
       key = Convert.ToBase64String(rmt.Key); 
       iv = Convert.ToBase64String(rmt.IV); 
      } 

      string encryptedData = _encrypt(DataForEncrypting, key, iv); 
      string unencryptedData = _decrypt(key, iv, HexString2Ascii(encryptedData)); 

      Console.WriteLine(unencryptedData); 
      Console.WriteLine(encryptedData); 
      Console.ReadKey(); 
     } 

     private static string _encrypt(string value, string key, string initVector) 
     { 
      byte[] buffer = ASCIIEncoding.ASCII.GetBytes(value); 
      byte[] encBuffer; 
      using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged()) 
      { 
       rmt.KeySize = 256; 
       rmt.BlockSize = 128; 
       rmt.Mode = System.Security.Cryptography.CipherMode.CBC; 
       rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126; 
       encBuffer = rmt.CreateEncryptor(Convert.FromBase64String(key), 
        Convert.FromBase64String(initVector)).TransformFinalBlock(buffer, 0, buffer.Length); 
      } 
      string encryptValue = ConvertToHex(ASCIIEncoding.ASCII.GetString(encBuffer)); 
      return encryptValue; 
     } 

     private static string _decrypt(string key, string initVector, string value) 
     { 
      byte[] hexBuffer = ASCIIEncoding.ASCII.GetBytes(value); 
      byte[] decBuffer; 
      using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged()) 
      { 
       rmt.KeySize = 256; 
       rmt.BlockSize = 128; 
       rmt.Mode = System.Security.Cryptography.CipherMode.CBC; 
       rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126; 
       decBuffer = rmt.CreateDecryptor(Convert.FromBase64String(key), 
        Convert.FromBase64String(initVector)).TransformFinalBlock(hexBuffer, 0, hexBuffer.Length); 
      } 

      return System.Text.ASCIIEncoding.ASCII.GetString(decBuffer); 
     } 

     private static string ConvertToHex(string asciiString) 
     { 
      string hex = ""; 
      foreach (char c in asciiString) 
      { 
       int tmp = c; 
       hex += String.Format("{0:x2}", (uint)System.Convert.ToUInt32(tmp.ToString())); 
      } 
      return hex; 
     } 

     private static string HexString2Ascii(string hexString) 
     { 
      StringBuilder sb = new StringBuilder(); 
      for (int i = 0; i <= hexString.Length - 2; i += 2) 
      { 
       sb.Append(Convert.ToString(Convert.ToChar(Int32.Parse(hexString.Substring(i, 2), System.Globalization.NumberStyles.HexNumber)))); 
      } 
      return sb.ToString(); 
     } 

    } 
} 

回答

2

你不應該使用ASCII字符編碼作爲一箇中間步驟;你應該改變你的函數從十六進制到ASCII(並且再回來),以從byte[]轉爲十六進制(並且再回來)。

private static string ConvertToHex(byte[] data) 
    { 
     string hex = ""; 
     foreach (byte b in data) 
     { 
      hex += b.ToString("X2"); 
     } 
     return hex; 
    } 

    private static byte[] HexString2ByteArray(string hexString) 
    { 
     byte[] output = new byte[hexString.Length/2]; 

     for (int i = 0; i <= hexString.Length - 2; i += 2) 
     { 
      output[i/2] = Convert.ToByte(hexString.Substring(i, 2), 16); 
     } 
     return output; 
    } 

作爲一個側面說明,有沒有你要找的陣列與更多的東西像緊湊的Base64的十六進制表示的原因是什麼?你在你的例子中使用Base64來傳輸密鑰和IV,所以我只是想知道什麼使你想要以十六進制的形式返回加密數據。

在任何情況下,這裏的東西應該爲你工作:

private static string _encrypt(string value, string key, string initVector) 
    { 
     byte[] buffer = Encoding.Unicode.GetBytes(value); 
     byte[] encBuffer; 
     using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged()) 
     { 
      rmt.KeySize = 256; 
      rmt.BlockSize = 128; 
      rmt.Mode = System.Security.Cryptography.CipherMode.CBC; 
      rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126; 
      encBuffer = rmt.CreateEncryptor(Convert.FromBase64String(key), 
       Convert.FromBase64String(initVector)).TransformFinalBlock(buffer, 0, buffer.Length); 
     } 
     string encryptValue = ConvertToHex(encBuffer); 
     return encryptValue; 
    } 

    private static string _decrypt(string key, string initVector, string value) 
    { 
     byte[] hexBuffer = HexString2ByteArray(value); 
     byte[] decBuffer; 
     using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged()) 
     { 
      rmt.KeySize = 256; 
      rmt.BlockSize = 128; 
      rmt.Mode = System.Security.Cryptography.CipherMode.CBC; 
      rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126; 
      decBuffer = rmt.CreateDecryptor(Convert.FromBase64String(key), 
       Convert.FromBase64String(initVector)).TransformFinalBlock(hexBuffer, 0, hexBuffer.Length); 
     } 

     return Encoding.Unicode.GetString(decBuffer); 
    } 
+0

使用的原因是,我被要求提供IV和重點用base64和十六進制的加密結果。 – jcs 2011-02-07 20:11:00

+0

@jcs:夠公平的;這應該爲你做的伎倆。請注意,Jon的答案在功能上會將加密值作爲base64字符串返回,而不是十六進制。 – 2011-02-07 20:17:10

5

你做的文本和數據之間太多的轉換,基本上是這樣。看看這個,例如:

string encryptValue = ConvertToHex(ASCIIEncoding.ASCII.GetString(encBuffer)); 

一旦你得到了一個ASCII字符串,你爲什麼會需要轉換是爲十六進制?它已經是文字了!但到那時你已經失去了數據。除非你真的需要以十六進制(在這種情況下遵循亞當的建議,並改變你的HexToAscii法取一個byte [],而不是字符串),你應該只使用Convert.ToBase64String:

string encryptValue = Convert.ToBase64String(encBuffer); 

使用Convert.FromBase64String在另一端解密時。你可以完全擺脫你的十六進制方法。

哦,一般來說我不會用開始...我幾乎總是用Encoding.UTF8來代替。目前,您將無法正確加密任何包含非ASCII字符(如重音符號)的字符串。

下面是您的測試程序的需要版本,以及其中的一些更改。請注意,名稱「密文」和「純文本」是在加密方面......它們仍然是二進制數據而不是文本!

using System; 
using System.Security.Cryptography; 
using System.Text; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     string DataForEncrypting = "this is a test"; 

     string key = string.Empty; 
     string iv = string.Empty; 

     using (RijndaelManaged rmt = new RijndaelManaged()) 
     { 
      rmt.KeySize = 256; 
      rmt.BlockSize = 128; 
      rmt.Mode = CipherMode.CBC; 
      rmt.Padding = PaddingMode.ISO10126; 
      rmt.GenerateKey(); 
      rmt.GenerateIV(); 
      key = Convert.ToBase64String(rmt.Key); 
      iv = Convert.ToBase64String(rmt.IV); 
     } 

     string encryptedData = _encrypt(DataForEncrypting, key, iv); 
     string unencryptedData = _decrypt(key, iv, encryptedData); 

     Console.WriteLine(unencryptedData); 
     Console.WriteLine(encryptedData); 
     Console.ReadKey(); 
    } 

    private static string _encrypt(string value, string key, string initVector) 
    { 
     using (RijndaelManaged rmt = new RijndaelManaged()) 
     { 
      rmt.KeySize = 256; 
      rmt.BlockSize = 128; 
      rmt.Mode = CipherMode.CBC; 
      rmt.Padding = PaddingMode.ISO10126; 
      byte[] plainText = Encoding.UTF8.GetBytes(value); 
      byte[] cipherText = rmt.CreateEncryptor(Convert.FromBase64String(key), 
                Convert.FromBase64String(initVector)) 
            .TransformFinalBlock(plainText, 0, plainText.Length); 
      return Convert.ToBase64String(cipherText); 
     } 
    } 

    private static string _decrypt(string key, string initVector, string value) 
    { 
     using (RijndaelManaged rmt = new RijndaelManaged()) 
     { 
      rmt.KeySize = 256; 
      rmt.BlockSize = 128; 
      rmt.Mode = CipherMode.CBC; 
      rmt.Padding = PaddingMode.ISO10126; 
      byte[] cipherText = Convert.FromBase64String(value); 
      byte[] plainText = rmt.CreateDecryptor(Convert.FromBase64String(key), 
                Convert.FromBase64String(initVector)) 
            .TransformFinalBlock(cipherText, 0, cipherText.Length); 
      return Encoding.UTF8.GetString(plainText); 
     } 
    } 
} 
2

您可能會避免與Decypting /加密和usign System.Text.Encoding的問題,並避免使用Base64編碼解決辦法,通過增加完全繞過在System.Text.Encoding微軟的轉換不匹配的一些方法,通過允許您加密內存中的實際字節而無需任何翻譯。

由於使用了這些,我避免了由System.Text.Encoding方法引起的填充錯誤,而無需使用Base64轉換。

private static Byte[] GetBytes(String SomeString) 
    { 
     Char[] SomeChars = SomeString.ToCharArray(); 
     Int32 Size = SomeChars.Length * 2; 
     List<Byte> TempList = new List<Byte>(Size); 
     foreach (Char Character in SomeChars) 
     { 
      TempList.AddRange(BitConverter.GetBytes(Character)); 
     } 
     return TempList.ToArray(); 
    } 
    private static String GetString(Byte[] ByteArray) 
    { 
     Int32 Size = ByteArray.Length/2; 
     List<Char> TempList = new List<Char>(Size); 
     for (Int32 i = 0; i < ByteArray.Length; i += 2) 
     { 
      TempList.Add(BitConverter.ToChar(ByteArray, i)); 
     } 
     return new String(TempList.ToArray()); 
    } 

以及他們是如何用加密

private static String Encrypt(String Test1, Byte[] Key, Byte[] IV) 
    { 
     Byte[] Encrypted; 
     using (AesCryptoServiceProvider AesMan = new AesCryptoServiceProvider()) 
     { 
      AesMan.Mode    = CipherMode.CBC; 
      AesMan.Padding   = PaddingMode.ISO10126; 
      ICryptoTransform EncThis = AesMan.CreateEncryptor(Key, IV); 

      using (MemoryStream msEncrypt = new MemoryStream()) 
      { 
       using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, EncThis, CryptoStreamMode.Write)) 
       { 

        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) 
        { 
         //Write all data to the stream. 
         swEncrypt.Write(Test1); 
        } 
        Encrypted = msEncrypt.ToArray(); 
       } 
      } 
     }; 
     return GetString(Encrypted); 
    } 

    private static String Decrypt(String Data, Byte[] Key, Byte[] IV) 
    { 
     String Decrypted; 
     using (AesCryptoServiceProvider AesMan = new AesCryptoServiceProvider()) 
     { 
      AesMan.Mode    = CipherMode.CBC; 
      AesMan.Padding   = PaddingMode.ISO10126; 
      ICryptoTransform EncThis = AesMan.CreateDecryptor(Key, IV); 

      using (MemoryStream msDecrypt = new MemoryStream(GetBytes(Data))) 
      { 
       using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, EncThis, CryptoStreamMode.Read)) 
       { 
        using (StreamReader srDecrypt = new StreamReader(csDecrypt)) 
        { 

         // Read the decrypted bytes from the decrypting stream 
         // and place them in a string. 
         Decrypted = srDecrypt.ReadToEnd(); 
        } 
       } 
      } 
     } 
     return Decrypted; 
    }