2010-12-28 18 views
6

Tôi đã viết một số mã hóa AES trong C# và tôi gặp sự cố khi mã hóa và giải mã đúng cách. Nếu tôi nhập "kiểm tra" làm cụm mật khẩu và "Dữ liệu này phải được giữ bí mật với mọi người!" Tôi nhận được ngoại lệ sau:Sử dụng mã hóa AES trong .NET - CryptographicException nói rằng padding không hợp lệ và không thể bị xóa

System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed. 
    at System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast) 
    at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) 
    at System.Security.Cryptography.CryptoStream.FlushFinalBlock() 
    at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing) 
    at System.IO.Stream.Close() 
    at System.IO.Stream.Dispose() 
    ... 

Và nếu tôi nhập nội dung nào đó dưới 16 ký tự, tôi sẽ không có đầu ra.

Tôi tin rằng tôi cần xử lý đặc biệt trong mã hóa vì AES là mật mã khối, nhưng tôi không chắc chính xác đó là gì và tôi không thể tìm thấy bất kỳ ví dụ nào trên web hiển thị như thế nào. Đây là mã của tôi:

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

public static class DatabaseCrypto 
{ 
    public static EncryptedData Encrypt(string password, string data) 
    { 
     return DatabaseCrypto.Transform(true, password, data, null, null) as EncryptedData; 
    } 

    public static string Decrypt(string password, EncryptedData data) 
    { 
     return DatabaseCrypto.Transform(false, password, data.DataString, data.SaltString, data.MACString) as string; 
    } 

    private static object Transform(bool encrypt, string password, string data, string saltString, string macString) 
    { 
     using (AesManaged aes = new AesManaged()) 
     { 
      aes.Mode = CipherMode.CBC; 
      aes.Padding = PaddingMode.PKCS7; 
      int key_len = aes.KeySize/8; 
      int iv_len = aes.BlockSize/8; 
      const int salt_size = 8; 
      const int iterations = 8192; 

      byte[] salt = encrypt ? new byte[salt_size] : Convert.FromBase64String(saltString); 
      if (encrypt) 
      { 
       new RNGCryptoServiceProvider().GetBytes(salt); 
      } 

      byte[] bc_key = new Rfc2898DeriveBytes("BLK" + password, salt, iterations).GetBytes(key_len); 
      byte[] iv = new Rfc2898DeriveBytes("IV" + password, salt, iterations).GetBytes(iv_len); 
      byte[] mac_key = new Rfc2898DeriveBytes("MAC" + password, salt, iterations).GetBytes(16); 

      aes.Key = bc_key; 
      aes.IV = iv; 

      byte[] rawData = encrypt ? Encoding.UTF8.GetBytes(data) : Convert.FromBase64String(data); 

      using (ICryptoTransform transform = encrypt ? aes.CreateEncryptor() : aes.CreateDecryptor()) 
      using (MemoryStream memoryStream = encrypt ? new MemoryStream() : new MemoryStream(rawData)) 
      using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, encrypt ? CryptoStreamMode.Write : CryptoStreamMode.Read)) 
      { 
       if (encrypt) 
       { 
        cryptoStream.Write(rawData, 0, rawData.Length); 

        return new EncryptedData(salt, mac_key, memoryStream.ToArray()); 
       } 
       else 
       { 
        byte[] originalData = new byte[rawData.Length]; 
        int count = cryptoStream.Read(originalData, 0, originalData.Length); 

        return Encoding.UTF8.GetString(originalData, 0, count); 
       } 
      } 
     } 
    } 
} 

public class EncryptedData 
{ 
    public EncryptedData() 
    { 
    } 

    public EncryptedData(byte[] salt, byte[] mac, byte[] data) 
    { 
     this.Salt = salt; 
     this.MAC = mac; 
     this.Data = data; 
    } 

    public EncryptedData(string salt, string mac, string data) 
    { 
     this.SaltString = salt; 
     this.MACString = mac; 
     this.DataString = data; 
    } 

    public byte[] Salt 
    { 
     get; 
     set; 
    } 

    public string SaltString 
    { 
     get { return Convert.ToBase64String(this.Salt); } 
     set { this.Salt = Convert.FromBase64String(value); } 
    } 

    public byte[] MAC 
    { 
     get; 
     set; 
    } 

    public string MACString 
    { 
     get { return Convert.ToBase64String(this.MAC); } 
     set { this.MAC = Convert.FromBase64String(value); } 
    } 

    public byte[] Data 
    { 
     get; 
     set; 
    } 

    public string DataString 
    { 
     get { return Convert.ToBase64String(this.Data); } 
     set { this.Data = Convert.FromBase64String(value); } 
    } 
} 

    static void ReadTest() 
    { 
     Console.WriteLine("Enter password: "); 
     string password = Console.ReadLine(); 

     using (StreamReader reader = new StreamReader("aes.cs.txt")) 
     { 
      EncryptedData enc = new EncryptedData(); 
      enc.SaltString = reader.ReadLine(); 
      enc.MACString = reader.ReadLine(); 
      enc.DataString = reader.ReadLine(); 

      Console.WriteLine("The decrypted data was: " + DatabaseCrypto.Decrypt(password, enc)); 
     } 
    } 

    static void WriteTest() 
    { 
     Console.WriteLine("Enter data: "); 
     string data = Console.ReadLine(); 
     Console.WriteLine("Enter password: "); 
     string password = Console.ReadLine(); 

     EncryptedData enc = DatabaseCrypto.Encrypt(password, data); 

     using (StreamWriter stream = new StreamWriter("aes.cs.txt")) 
     { 
      stream.WriteLine(enc.SaltString); 
      stream.WriteLine(enc.MACString); 
      stream.WriteLine(enc.DataString); 

      Console.WriteLine("The encrypted data was: " + enc.DataString); 
     } 
    } 

Trả lời

13

Khi sử dụng một mật mã khối như AES trong một chế độ mà đòi hỏi đệm, như CBC, bạn phải lưu ý rằng sản lượng sẽ luôn là một bội số của kích thước khối. Để thực hiện việc này, các chế độ đệm như PKCS7 sẽ thêm một số byte vào mật mã ở cuối quá trình mã hóa. Nhưng bạn phải cho người mã hóa biết khi nào kết thúc xảy ra. Để làm như vậy, tất cả các bạn phải làm là chèn tuyên bố

cryptoStream.FlushFinalBlock(); 

sau

cryptoStream.Write(rawData, 0, rawData.Length); 

PS:

Có lẽ nó chỉ là để gỡ lỗi, nhưng phương pháp hệ muối của bạn tạo ra chính xác cùng muối mỗi lần.

+0

Wow, đơn giản như vậy, eh? Tôi nghĩ rằng nó có thể là một cái gì đó để làm với FlushFinalBlock nhưng tôi đã không chắc chắn. Tôi nghĩ rằng tôi đã thêm nó trước nhưng có lẽ tôi vừa nhập sai mật khẩu. Tôi nên tạo ra một số xét nghiệm đơn vị ngẫu nhiên. :) Ngoài ra tôi biết muối là như nhau - Tôi đã không chắc chắn làm thế nào để tạo ra một ngẫu nhiên nhưng chưa tìm thấy 'RNGCryptoServiceProvider' sau khi viết mã đó. Cảm ơn bạn đã chỉ ra. –

+0

Vì vậy, nhiều ví dụ trong msdn và stackoverflow không phải là rất khỏe mạnh, và câu trả lời này đã lưu ngày của tôi. FlushFinalBlock(). Wow. – ZZZ

+0

Đã gói CryptoStream trong một StreamWriter ... 'cryptoStream.FlushFinalBlock();' cần phải được gọi là bất kể. Cảm ơn - đây là một tiết kiệm thời gian rất lớn! – trousyt

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