2014-10-20 15 views
6

Tôi hiện đang làm việc về triển khai AES trong C#. Phương thức mã hóa có hai tham số: một chuỗi và một mật khẩu. Tôi đang lấy chuỗi cung cấp và chuyển đổi nó thành một mảng byte, vì vậy tôi có thể sử dụng nó sau này để ghi dữ liệu vào một luồng với BinaryWriter.Làm thế nào để nhận được byte chính xác và nhất quán từ một chuỗi mã hóa AES?

Vấn đề là khi tôi sử dụng Convert.FromBase64String(string) tôi nhận được FormatException: Invalid length. và khi tôi sử dụng Encoding.UTF8.GetBytes(string) phương thức giải mã của tôi sẽ ném và ngoại lệ PKCS7.Padding không hợp lệ.

Tôi đã cố gắng giải quyết vấn đề này trong vài ngày qua. Tôi đã đọc gần các câu hỏi vô hạn trong stackoverflow.com và các trang web khác, nhưng tôi vẫn không biết cách đáng tin cậy nhất để giải quyết vấn đề này là gì.

Các chuỗi sẽ được sử dụng trong chương trình này được giới hạn trong các câu (ví dụ: "Cái gì đó để mã hóa") và các số (ví dụ: "12345").

Cảm ơn bạn trước, đây là đoạn code tôi có vào thời điểm này trong thời gian:

public class AESProvider { 

    public byte[] EncryptStringToBytes_Aes(string plainText, string Key) 
    { 
     // Check arguments. 
     if (plainText == null || plainText.Length <= 0) 
      throw new ArgumentNullException("plainText"); 
     if (Key == null || Key.Length <= 0) 
      throw new ArgumentNullException("Key"); 
     byte[] plainTextInBytes = Convert.FromBase64String(plainText); 
     byte[] encrypted; 

     //Create an Aes object 
     //with the specified key and IV. 

     using (Aes aesAlg = Aes.Create()) 
     { 
      aesAlg.GenerateIV(); 
      byte[] IV = aesAlg.IV; 
      //The Salt will be the first 8 bytes of the IV. 
      byte[] theSalt = new byte[8]; 
      Array.Copy(IV,theSalt,8); 
      //A key for AES is generated by expanding the password using the following method. 
      Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(Key,theSalt); 
      byte[] aesKey = keyGen.GetBytes(16); 
      aesAlg.Key = aesKey; 

      // Create a decrytor to perform the stream transform. 
      ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, IV); 

      // Create the streams used for encryption. 
      using (MemoryStream msEncrypt = new MemoryStream()) 
      { 
       using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) 
       { 
        using (BinaryWriter swEncrypt = new BinaryWriter(csEncrypt)) 
        { 

         //Write all data to the stream. 
         swEncrypt.Write(plainTextInBytes); 
        } 
        encrypted = msEncrypt.ToArray(); 
       } 
      } 
      // Prepend the IV to the ciphertext so it can be used in the decryption process. 
      using (MemoryStream ivPlusCipher = new MemoryStream()) 
      { 
       using (BinaryWriter tBinaryWriter = new BinaryWriter(ivPlusCipher)) 
       { 
        tBinaryWriter.Write(IV); 
        tBinaryWriter.Write(encrypted); 
        tBinaryWriter.Flush(); 
       } 
       return ivPlusCipher.ToArray(); 
      } 
     } 
    } 

    public byte[] DecryptStringFromBytes_Aes(byte[] cipherText, string Key) 
    { 
     // Check arguments. 
     if (cipherText == null || cipherText.Length <= 0) 
      throw new ArgumentNullException("cipherText"); 
     if (Key == null || Key.Length <= 0) 
      throw new ArgumentNullException("Key"); 
     // Declare the string used to hold 
     // the decrypted text. 
     byte[] decrypted; 

     // Create an Aes object 
     // with the specified key and IV. 

     // Create the streams used for decryption. 

     using (Aes aesAlg = Aes.Create()) 
     { 
      aesAlg.Mode = CipherMode.CBC; 
      aesAlg.Padding = PaddingMode.PKCS7; 
      //Grab IV from ciphertext 
      byte[] IV = new byte[16]; 
      Array.Copy(cipherText,0,IV,0,16); 
      //Use the IV for the Salt 
      byte[] theSalt = new byte[8]; 
      Array.Copy(IV,theSalt,8); 
      Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(Key,theSalt); 
      byte[] aesKey = keyGen.GetBytes(16); 
      aesAlg.Key = aesKey; 

      // Create a decrytor to perform the stream transform. 
      ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, IV); 

      using (MemoryStream msDecrypt = new MemoryStream()) 
      { 
       using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write)) 
       { 
        using (BinaryWriter srDecrypt = new BinaryWriter(csDecrypt)) 
        { 
         //Decrypt the ciphertext 
         srDecrypt.Write(cipherText, IV.Length, (cipherText.Length - IV.Length)); 
        } 
        decrypted = msDecrypt.ToArray(); 
        return decrypted; 
       } 
      } 
     } 
    } 
} 
+0

'DecryptStringFromBytes' của bạn không trả lại chuỗi, tại sao? –

+1

Câu hỏi quan trọng nhất là và sẽ vẫn là: những gì được đặt trong chuỗi đầu vào mà bạn có nghĩa vụ phải giải mã. Ý tôi là, nếu nó là hệ thập lục phân thì không có câu trả lời nào đúng. Vui lòng cung cấp một ví dụ! –

+0

Cảm ơn bạn đã nhập. Ví dụ về các chuỗi đã được thêm vào câu hỏi! – Mandos

Trả lời

6

Bạn cần phải chuyển đổi giữa byte và chuỗi trước và sau khi mã hóa/giải mã. Đây không phải là hoạt động tương tự, và bạn không nên sử dụng cùng một phương pháp.

Khi mã hóa bạn bắt đầu bằng chuỗi tùy ý. Chuyển đổi thành byte [] sử dụng Encoding.UTF8.GetBytes(). Mã hóa nó. Kết quả byte [] bây giờ có thể được chuyển đổi thành một chuỗi sử dụng Convert.ToBase64String().

Khi giải mã, bạn bắt đầu với chuỗi mã hóa Base64. Giải mã mã này thành byte [] sử dụng Convert.FromBase64String(). Giải mã nó. Bây giờ bạn có mã hóa UTF-8 của chuỗi ban đầu, bạn có thể giải mã bằng cách sử dụng Encoding.UTF8.GetString().

Hãy nhớ rằng:

  • Encoding.UTF8 hoạt động để chuyển đổi chuỗi tùy ý để byte-mảng (nhưng nó chỉ có thể chuyển đổi byte-mảng chứa thực tế UTF8-mã hóa trở lại).
  • Chuyển đổi. [Đến/Từ] Base64String hoạt động để chuyển đổi mảng byte tùy ý thành chuỗi (nhưng nó chỉ có thể chuyển đổi các chuỗi có chứa mã hóa Base64 thực).
+1

"Để tất cả các câu hỏi rất dài, redacted, được đăng trong Stack Overflow luôn luôn có một câu trả lời rất đơn giản khiến bạn cảm thấy rất ngu ngốc. " Albert Einstein. Cảm ơn bạn đã trả lời, nó làm việc rất đẹp. – Mandos

2

Convert.FromBase64String(string); dự kiến ​​sẽ nhận được một chuỗi được tạo ra bởi Convert.ToBase64String(byte[]); đi qua trong một chuỗi tùy ý sẽ không làm việc.

Giải pháp dễ nhất là thay thế BinaryWriterBinaryReader bằng StreamWriterStreamReader và hoàn toàn không thực hiện bất kỳ chuyển đổi nào.

public byte[] EncryptStringToBytes_Aes(string plainText, string Key) 
{ 
    // Check arguments. 
    if (plainText == null || plainText.Length <= 0) 
     throw new ArgumentNullException("plainText"); 
    if (Key == null || Key.Length <= 0) 
     throw new ArgumentNullException("Key"); 


    //Create an Aes object 
    //with the specified key and IV. 

    using (Aes aesAlg = Aes.Create()) 
    { 
     aesAlg.GenerateIV(); 
     byte[] IV = aesAlg.IV; 
     //The Salt will be the first 8 bytes of the IV. 
     byte[] theSalt = new byte[8]; 
     Array.Copy(IV,theSalt,8); 
     //A key for AES is generated by expanding the password using the following method. 
     Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(Key,theSalt); 
     byte[] aesKey = keyGen.GetBytes(16); 
     aesAlg.Key = aesKey; 

     // Create a decrytor to perform the stream transform. 
     ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, IV); 

     // Create the streams used for encryption. 
     using (MemoryStream msEncrypt = new MemoryStream()) 
     { 
      //You can write the IV here and not need to do it later. 
      msEncrypt.Write(IV, 0, IV.Length); 

      using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) 
      { 
       using (StreamWriter swEncrypt = new StreamWriter (csEncrypt)) 
       {  
        //Write all data to the stream. 
        swEncrypt.Write(plainText); 
       } 
      } 

      //Move this outside of the using statement for CryptoStream so it is flushed and dipsoed. 
      return msEncrypt.ToArray(); 
     } 
    } 
} 

Ngoài ra, chức năng giải mã của bạn là thực sự cố gắng để mã hóa văn bản trong một thời gian thứ 2, bạn cần phải vượt qua mảng byte vào constructor của msDecrypt và đặt nó trong chế độ giải mã.

public string DecryptStringFromBytes_Aes(byte[] cipherText, string Key) 
{ 
    // Check arguments. 
    if (cipherText == null || cipherText.Length <= 0) 
     throw new ArgumentNullException("cipherText"); 
    if (Key == null || Key.Length <= 0) 
     throw new ArgumentNullException("Key"); 

    // Create an Aes object 
    // with the specified key and IV. 

    // Create the streams used for decryption. 

    using (Aes aesAlg = Aes.Create()) 
    { 
     aesAlg.Mode = CipherMode.CBC; 
     aesAlg.Padding = PaddingMode.PKCS7; 
     //Grab IV from ciphertext 
     byte[] IV = new byte[16]; 
     Array.Copy(cipherText,0,IV,0,16); 
     //Use the IV for the Salt 
     byte[] theSalt = new byte[8]; 
     Array.Copy(IV,theSalt,8); 
     Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(Key,theSalt); 
     byte[] aesKey = keyGen.GetBytes(16); 
     aesAlg.Key = aesKey; 

     // Create a decrytor to perform the stream transform. 
     ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, IV); 

     //You can chain using statements like this to make the code easier to read. 
     using (MemoryStream msDecrypt = new MemoryStream(cipherText)) 
     using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) //Notice this is Read mode not Write mode. 
     using (StreamReader srDecrypt = new StreamReader(csDecrypt)) 
     { 
      //Decrypt the ciphertext 
      return srDecrypt.ReadToEnd(); 
     } 
    } 
} 

Có thể có các lỗi khác với mã của bạn, nhưng ít nhất điều này giúp bạn đi đúng hướng.

+0

Cảm ơn bạn đã trả lời. Nhưng tôi nghĩ rằng có một vấn đề với cách tiếp cận này (sửa tôi nếu tôi sai vì tôi không biết nhiều về các luồng trong C#) ... Trong quá trình mã hóa bạn mã hóa IV cùng với thông điệp, vì vậy trong quá trình giải mã khi phương thức Array.Copy được gọi là IV sẽ không phải là bản gốc, nhưng nó sẽ là 16 byte đầu tiên của bản mã. Vì vậy, đây sẽ là một vấn đề. Ngoài ra khi giải mã bản mã, mã của bạn sẽ giải mã IV cùng với thông điệp (dễ giải quyết nhưng tôi vẫn không biết nếu có cách nào tốt hơn). – Mandos

+0

Có cách nào để tôi có thể sử dụng StreamReader/Writer và vẫn cung cấp cùng chức năng mà mã ban đầu của tôi có liên quan đến IV không? – Mandos

+0

Có, tôi vừa hợp nhất mã của bạn, Trình đọc và ghi luồng không ảnh hưởng đến phần IV. Chỉ cần sử dụng một Binary Writer cho phần IV của bạn nếu bạn muốn (tôi chỉ nghĩ rằng nó là khá hiệu quả cách làm nó như bạn đang tạo thêm byte [] trong bộ nhớ đằng sau các khoa học không cần thiết.). Chỉ cần xóa 'msEncrypt.Write (IV, 0, IV.Length);' và sau đó đặt trở lại trong khối cũ của bạn, nơi bạn đã sao chép IV. –

2

Nhìn vào đường dây của bạn

public byte[] EncryptStringToBytes_Aes(string plainText, string Key) 
byte[] plainTextInBytes = Convert.FromBase64String(plainText); 

Arbitrary văn bản đơn giản sẽ không được một cơ sở 64 mã hóa chuỗi. Thậm chí nếu nó được coi là cơ sở 64 văn bản được mã hóa, thông báo lỗi của bạn chỉ ra rằng chiều dài là không chia hết cho 4

FormatException
Chiều dài của s, bỏ qua ký tự trắng-không gian, không được không hay bội số của 4. -hoặc- Định dạng của s không hợp lệ. s chứa ký tự không phải là cơ sở-64, nhiều hơn hai ký tự đệm hoặc một ký tự khoảng trắng không nằm trong các ký tự đệm.

http://msdn.microsoft.com/en-us/library/system.convert.frombase64string(v=vs.110).aspx

Nếu nó là một cơ sở 64 chuỗi mã hóa, bạn cần phải pad nó accorgingly

http://en.wikipedia.org/wiki/Base64

Các vấn đề liên quan