2012-02-11 14 views
15

Tôi phải mã hóa, lưu trữ và sau đó giải mã các tệp lớn. Cách tốt nhất để làm điều đó là gì? Tôi nghe mã hóa RSA rất tốn kém và được khuyên nên sử dụng RSA để mã hóa khóa AES và sau đó sử dụng khóa AES để mã hóa các tệp lớn. Bất kỳ đề xuất với ví dụ sẽ là tuyệt vời.Mã hóa/giải mã các tệp lớn (.NET)

+0

Dưới đây là một ví dụ [bài viết trên blog] (http://www.technical-recipes.com/2013/using-rsa-to- mã hóa-lớn-dữ liệu-tệp-in-c /), với tải về [Visual Studio 2010 dự án] (http://www.technical-recipes.com/Downloads/crypt.7z) mà chỉ làm điều đó. – AndyUK

Trả lời

10

Nói chung chiến lược bạn đã mô tả được sử dụng khi dữ liệu sẽ được mã hóa trên một máy (như máy chủ) và sau đó được giải mã bằng máy khác (máy khách). Máy chủ sẽ mã hóa dữ liệu bằng cách sử dụng mã hóa khóa đối xứng (để thực hiện) với khóa mới được tạo và mã hóa khóa đối xứng này bằng khóa công khai (khớp với khóa riêng của khách hàng). Máy chủ gửi cho khách hàng cả dữ liệu được mã hóa và khóa đối xứng được mã hóa. Máy khách có thể giải mã khóa đối xứng bằng khóa riêng và sau đó sử dụng khóa đối xứng này để giải mã dữ liệu. Nếu bạn đang mã hóa và giải mã dữ liệu trên cùng một máy, bạn có thể không sử dụng cả RSA và AES vì bạn sẽ không cố chuyển khóa mã hóa sang một máy khác.

+1

Cảm ơn, nhưng vấn đề của tôi hơi khác một chút. Tôi muốn mã hóa (các) tệp lớn và sau đó lưu trữ và mã hóa (tức là bảo mật) là cần thiết trong trường hợp bộ nhớ bị xâm phạm. Đối với giao tiếp máy chủ khách hàng tôi gửi trong khóa AES được mã hóa bằng RSA, làm thế nào để nó transalte trong kịch bản của tôi? Tôi có lưu trữ khóa AES như là một phần của các byte đang chờ xử lý hoặc bổ sung của blob được mã hóa khóa đối xứng không? – kalrashi

+0

Một ví dụ tuyệt vời thực sự được tìm thấy trên Tài liệu MSDN X509Certificate2 (Tôi đã tìm trong RSACryptoProvider và các lớp liên quan): – kalrashi

8

Một sinh vật lớn là nhỏ nhắn, mặc dù chúng ta đều biết tốn kém khi chúng ta nhìn thấy nó. Nháy mắt.

Hãy thử điểm chuẩn một cái gì đó như sau trong môi trường của bạn và xem nơi bạn đang ở:

EDIT 2012/02/13: Mã này đã được cập nhật như tôi đã trở thành (không đáng kể) thông minh hơn và cũng nhận thấy một vài lỗi của những con cut'n'paste đã len lỏi. Mea culpa.

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

... 

    // Rfc2898DeriveBytes constants: 
    public readonly byte[] salt = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Must be at least eight bytes. MAKE THIS SALTIER! 
    public const int iterations = 1042; // Recommendation is >= 1000. 

    /// <summary>Decrypt a file.</summary> 
    /// <remarks>NB: "Padding is invalid and cannot be removed." is the Universal CryptoServices error. Make sure the password, salt and iterations are correct before getting nervous.</remarks> 
    /// <param name="sourceFilename">The full path and name of the file to be decrypted.</param> 
    /// <param name="destinationFilename">The full path and name of the file to be output.</param> 
    /// <param name="password">The password for the decryption.</param> 
    /// <param name="salt">The salt to be applied to the password.</param> 
    /// <param name="iterations">The number of iterations Rfc2898DeriveBytes should use before generating the key and initialization vector for the decryption.</param> 
    public void DecryptFile(string sourceFilename, string destinationFilename, string password, byte[] salt, int iterations) 
    { 
     AesManaged aes = new AesManaged(); 
     aes.BlockSize = aes.LegalBlockSizes[0].MaxSize; 
     aes.KeySize = aes.LegalKeySizes[0].MaxSize; 
     // NB: Rfc2898DeriveBytes initialization and subsequent calls to GetBytes must be eactly the same, including order, on both the encryption and decryption sides. 
     Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterations); 
     aes.Key = key.GetBytes(aes.KeySize/8); 
     aes.IV = key.GetBytes(aes.BlockSize/8); 
     aes.Mode = CipherMode.CBC; 
     ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV); 

     using (FileStream destination = new FileStream(destinationFilename, FileMode.CreateNew, FileAccess.Write, FileShare.None)) 
     { 
      using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write)) 
      { 
       try 
       { 
        using (FileStream source = new FileStream(sourceFilename, FileMode.Open, FileAccess.Read, FileShare.Read)) 
        { 
         source.CopyTo(cryptoStream); 
        } 
       } 
       catch (CryptographicException exception) 
       { 
        if (exception.Message == "Padding is invalid and cannot be removed.") 
         throw new ApplicationException("Universal Microsoft Cryptographic Exception (Not to be believed!)", exception); 
        else 
         throw; 
       } 
      } 
     } 
    } 

    /// <summary>Encrypt a file.</summary> 
    /// <param name="sourceFilename">The full path and name of the file to be encrypted.</param> 
    /// <param name="destinationFilename">The full path and name of the file to be output.</param> 
    /// <param name="password">The password for the encryption.</param> 
    /// <param name="salt">The salt to be applied to the password.</param> 
    /// <param name="iterations">The number of iterations Rfc2898DeriveBytes should use before generating the key and initialization vector for the decryption.</param> 
    public void EncryptFile(string sourceFilename, string destinationFilename, string password, byte[] salt, int iterations) 
    { 
     AesManaged aes = new AesManaged(); 
     aes.BlockSize = aes.LegalBlockSizes[0].MaxSize; 
     aes.KeySize = aes.LegalKeySizes[0].MaxSize; 
     // NB: Rfc2898DeriveBytes initialization and subsequent calls to GetBytes must be eactly the same, including order, on both the encryption and decryption sides. 
     Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterations); 
     aes.Key = key.GetBytes(aes.KeySize/8); 
     aes.IV = key.GetBytes(aes.BlockSize/8); 
     aes.Mode = CipherMode.CBC; 
     ICryptoTransform transform = aes.CreateEncryptor(aes.Key, aes.IV); 

     using (FileStream destination = new FileStream(destinationFilename, FileMode.CreateNew, FileAccess.Write, FileShare.None)) 
     { 
      using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write)) 
      { 
       using (FileStream source = new FileStream(sourceFilename, FileMode.Open, FileAccess.Read, FileShare.Read)) 
       { 
        source.CopyTo(cryptoStream); 
       } 
      } 
     } 
    } 
+0

Ví dụ này chỉ mã hóa/giải mã bằng AES.Bạn có nghĩa là để nói thử điều tương tự với RSA và hồ sơ cả hai? – kalrashi

+0

Tùy thuộc vào bạn. Câu hỏi của bạn không giải thích được (các) mối đe dọa cảm nhận đối với dữ liệu, kích thước của tệp, bất kỳ chuyển mạng hoặc tiếp xúc nào, tài nguyên xử lý sẵn có, .... Trong trường hợp này, có vẻ như bạn sẽ làm tốt để thu thập ít nhất một dữ liệu điểm chuẩn nhỏ về chi phí mã hóa trong môi trường của bạn và làm việc từ đó. Những người khác đã xúc động trên mật mã khóa công khai so với khóa cá nhân và sức mạnh tương đối của các thuật toán khác nhau. – HABO

+2

Để tránh "Đệm không hợp lệ và không thể xóa được". ngoại lệ, bên mã hóa phải gọi 'source.FlushFinalBlock()' sau 'source.CopyTo (crytoStream);' nếu không bạn sẽ thực sự sai khối cuối cùng. – DipSwitch

3

Giống như bạn nghe bất đối xứng mật mã, như RSA, là chậm hơn nhiều so với mật mã đối xứng (ví dụ AES) nhưng nó không có đó là lợi thế (quản lý chủ chốt đơn giản hơn, ví dụ như một khóa bí mật duy nhất để bảo vệ).

Điều quan trọng (chơi chữ) là sử dụng lợi thế của cả hai (khóa riêng không đối xứng và tốc độ đối xứng) trong khi bỏ qua sự bất tiện của phím khác (nhiều khóa bí mật và tốc độ chậm).

Bạn có thể thực hiện điều này bằng cách sử dụng RSA một lần cho mỗi tệp (không có tác động hiệu suất lớn) để mã hóa khóa bí mật (đối xứng) được sử dụng để mã hóa tệp lớn của bạn. Điều này * gói của khóa đối xứng cho phép bạn chỉ quản lý một khóa riêng, duy nhất.

Đây là liên kết đến cũ của tôi (nhưng vẫn đúng) blog post cung cấp ví dụ để thực hiện việc này bằng C# và khung công tác .NET (Microsoft of Mono).

+0

Cảm ơn. Ví dụ này khá hữu ích. – kalrashi

10

này có thể giúp

/// Encrypts a file using Rijndael algorithm. 
///</summary> 
///<param name="inputFile"></param> 
///<param name="outputFile"></param> 
private void EncryptFile(string inputFile, string outputFile) 
{ 

    try 
    { 
     string password = @"myKey123"; // Your Key Here 
     UnicodeEncoding UE = new UnicodeEncoding(); 
     byte[] key = UE.GetBytes(password); 

     string cryptFile = outputFile; 
     FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create); 

     RijndaelManaged RMCrypto = new RijndaelManaged(); 

     CryptoStream cs = new CryptoStream(fsCrypt, 
      RMCrypto.CreateEncryptor(key, key), 
      CryptoStreamMode.Write); 

     FileStream fsIn = new FileStream(inputFile, FileMode.Open); 

     int data; 
     while ((data = fsIn.ReadByte()) != -1) 
      cs.WriteByte((byte)data); 


     fsIn.Close(); 
     cs.Close(); 
     fsCrypt.Close(); 
    } 
    catch 
    { 
     MessageBox.Show("Encryption failed!", "Error"); 
    } 
} 

/// 
/// Decrypts a file using Rijndael algorithm. 
///</summary> 
///<param name="inputFile"></param> 
///<param name="outputFile"></param> 
private void DecryptFile(string inputFile, string outputFile) 
{ 

    { 
     string password = @"myKey123"; // Your Key Here 

     UnicodeEncoding UE = new UnicodeEncoding(); 
     byte[] key = UE.GetBytes(password); 

     FileStream fsCrypt = new FileStream(inputFile, FileMode.Open); 

     RijndaelManaged RMCrypto = new RijndaelManaged(); 

     CryptoStream cs = new CryptoStream(fsCrypt, 
      RMCrypto.CreateDecryptor(key, key), 
      CryptoStreamMode.Read); 

     FileStream fsOut = new FileStream(outputFile, FileMode.Create); 

     int data; 
     while ((data = cs.ReadByte()) != -1) 
      fsOut.WriteByte((byte)data); 

     fsOut.Close(); 
     cs.Close(); 
     fsCrypt.Close(); 

    } 
} 

nguồn: http://www.codeproject.com/Articles/26085/File-Encryption-and-Decryption-in-C

+2

Mã này có một số vấn đề nghiêm trọng: a) Nó không hoạt động nếu mật khẩu không có đủ độ dài, nhưng vấn đề là mật khẩu thường là entropy thấp và do đó không thể sử dụng trực tiếp làm khóa. b) Sử dụng khóa như IV là làm cho mã hóa xác định này và do đó không an toàn về mặt ngữ nghĩa. IV phải được tạo ngẫu nhiên. Nó không phải là bí mật, nhưng chỉ có thể đoán trước được, vì vậy nó có thể được viết đơn giản ở phía trước của bản mã và được sử dụng trong quá trình giải mã. c) Không xác thực! Bản mã có thể được thay đổi mà không cần người nhận phát hiện điều này. –