2012-02-11 17 views
15

大きなファイルを暗号化して保存してから、後で復号化する必要があります。それをする最善の方法は何ですか?私はRSA暗号化が高価であると聞き、RSAを使ってAES鍵を暗号化し、AES鍵を使って大きなファイルを暗号化することを勧めました。例を挙げればどんな提案も素晴らしいでしょう。大きなファイルの暗号化/復号化(.NET)

+0

は一例であり、[ブログの投稿](http://www.technical-recipes.com/2013/using-rsa-to-ダウンロード可能な[Visual Studio 2010プロジェクト](http://www.technical-recipes.com/Downloads/crypt.7z)でダウンロードできます。 – AndyUK

答えて

10

説明した方法は、データがあるマシン(サーバーなど)で暗号化され、次に別のマシン(クライアント)によって復号化される場合に使用されます。サーバーは、新しく生成された鍵で対称鍵暗号化(パフォーマンス用)を使用してデータを暗号化し、この対称鍵を公開鍵(クライアントの秘密鍵に一致)で暗号化します。サーバーは、クライアントに暗号化されたデータと暗号化対称鍵の両方を送信します。クライアントは、その秘密鍵で対称鍵を復号化し、この対称鍵を使用してデータを復号化することができます。 同じマシン上のデータを暗号化して解読する場合は、RSAとAESの両方を使用して暗号鍵を別のマシンに渡そうとしない場合があります。

+1

ありがとう、しかし私の問題は少し異なります。大容量のファイルを暗号化して保存する場合、ストレージが侵害された場合に備えて暗号化(つまりセキュリティ)が必要です。クライアントサーバー通信では、RSAで暗号化されたAESキーを送信しますが、私のシナリオではどのように変換されますか?対称鍵で暗号化されたBLOBのプリフェーディングまたは追加バイトの一部としてAESキーを保存しますか? – kalrashi

+0

実際にX509Certificate2 MSDN Doc(RSACryptoProviderおよび関連するクラスを探していた)の素晴らしい例が見つかりました: – kalrashi

8

私たちがそれを見ると、私たちはすべて高価であるとはいえ、ある生物は小さいです。ウインクウインク。

お使いの環境で、次のようなものをベンチマーク試してみて、あなたがでている場所を参照してください。

EDIT 2012年2月13日:コードは賢くも注目(いつの間にか)私はなってきたように更新されましたいくつかのcut'n'pasteエラーがうかがっていた。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

この例では、AESを使用した暗号化/復号化のみを行っています。RSAとプロファイルの両方で同様のことを試してみることを意味しますか? – kalrashi

+0

それはあなた次第です。あなたの質問は、データ、ファイルのサイズ、ネットワーク転送や関連する暴露、利用可能な処理リソースなどに対する認識された脅威については説明しませんでした。この場合、環境内の暗号化コストに関するベンチマークデータを少なくとも収集し、そこから作業することはうまくいくように思えます。他のものは、公開鍵と秘密鍵の暗号化と、異なるアルゴリズムの相対的な強さに触れてきました。 – HABO

+2

「埋め込みが無効で、削除できません」という問題を回避するには例外として、暗号化側は 'source.CopyTo(crytoStream);の後に' source.FlushFinalBlock() 'を呼び出さなければなりません。 – DipSwitch

3

あなたは、非対称暗号を聞いたように、RSAのように、対称暗号(例えばAES)よりもはるかに遅いですが、それはそれが利点(単純鍵管理、保護するために、例えば、単一の秘密鍵)です持っています。

鍵は、他の鍵(多くの秘密鍵と低速)の不都合を無視して、両方の利点(非対称の秘密鍵と対称の速度)を使用することです。

大容量のファイルを暗号化する(はるかに高速に)ために使用される(対称)秘密鍵を暗号化するために、ファイルごとに1回(巨大なパフォーマンスへの影響なし)RSAを使用します。この* の対称キーのをラップすると、1つの秘密キーしか管理できません。

これは、C#と.NETフレームワーク(Microsoft of Mono)を使用してこれを行う例を示した、私の古い(ただしまだ真実の)blog postへのリンクです。これは役立つかもしれ

+0

ありがとうございます。この例はとても役に立ちました。 – kalrashi

10

/// 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(); 

    } 
} 

ソース:ここhttp://www.codeproject.com/Articles/26085/File-Encryption-and-Decryption-in-C

+2

このコードにはいくつかの重大な問題があります:a)パスワードの長さが十分でない場合は動作しませんが、通常はパスワードが低いエントロピーでキーとして直接使用できないという問題があります。 b)キーをIVとして使用すると、この決定論的な暗号化が行われるため、意味的に安全ではありません。 IVはランダムに生成されなければならない。それは秘密である必要はなく、予測不能なだけなので、単純に暗号文の前に書き込まれ、復号化中に使用されます。 c)認証なし!受信者がこれを検出することなく、暗号文を変更することができます。 –