2011-02-07 92 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; 
    }