web-dev-qa-db-ja.com

大きなファイルの暗号化/復号化(.NET)

大きなファイルを暗号化し、保存し、後で解読する必要があります。それを行う最良の方法は何ですか? RSA暗号化は高価であると聞きました。RSAを使用してAESキーを暗号化し、次にAESキーを使用して大きなファイルを暗号化することをお勧めしました。例付きの提案は素晴らしいでしょう。

24
kalrashi

ある生物の大きさは別の生物の小柄なものですが、私たちはそれを見ると高価だと知っています。ウインクウインク。

環境内で次のようなベンチマークを行って、現在の場所を確認してください。

編集2/13/2012:コードは(気づかないうちに)よりスマートになり、また、忍び込んでいるいくつかのカットアンドペーストエラーに気づいたので更新されました。Meaculpa。

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);
                }
            }
        }
    }
14
HABO

これは役立つかもしれません

/// 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

13
Muhammad Alaa

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

10
Matt Dearing

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

キー(しゃれを目的とする)は、他の不便(多くの秘密キーと低速)を無視しながら、両方の利点(非対称の秘密鍵と対称の速度)を使用することです。

これを行うには、ファイルごとに1回(パフォーマンスに大きな影響はありません)RSAを使用して、大きなファイルの暗号化(はるかに高速)に使用される(対称)秘密鍵を暗号化します。この対称キーの*wrappingを使用すると、単一の秘密キーのみを管理できます。

ここに私の古い(しかしまだ真実です)へのリンクがあります ブログ投稿 これは、C#と.NETフレームワーク(Microsoft of Mono)を使用してこれを行う例を示しています。

4
poupou