web-dev-qa-db-ja.com

バッファリングされた読み取りでMD5(または他の)ハッシュを計算することは可能ですか?

非常に大きなファイル(ギガバイト)のチェックサムを計算する必要があります。これは、次の方法を使用して実行できます。

    private byte[] calcHash(string file)
    {
        System.Security.Cryptography.HashAlgorithm ha = System.Security.Cryptography.MD5.Create();
        FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);
        byte[] hash = ha.ComputeHash(fs);
        fs.Close();
        return hash;
    }

ただし、ファイルは通常、バッファリングされた方法で直前に書き込まれます(たとえば、一度に32MBを書き込む)。書き込みと同時にMD5(または他の)ハッシュを計算できるハッシュ関数のオーバーライドを見たと確信しています。つまり、1つのバッファーのハッシュを計算し、その結果のハッシュを次の反復にフィードします。 。

このようなもの:(擬似コードっぽい)

byte [] hash = new byte [] { 0,0,0,0,0,0,0,0 };
while(!eof)
{
   buffer = readFromSourceFile();
   writefile(buffer);
   hash = calchash(buffer, hash);
}

ハッシュは、ファイル全体でcalcHash関数を実行することによって達成されるものと同じようになりました。

さて、.Net 3.5 Frameworkでそのようなオーバーライドを見つけることができません、私は夢を見ていますか?それは存在したことがありませんか、それとも私は検索がお粗末ですか?書き込みとチェックサム計算の両方を同時に実行する理由は、ファイルが大きいために意味があるためです。

35
sindre j

TransformBlockメソッドとTransformFinalBlockメソッドを使用して、データをチャンクで処理します。

// Init
MD5 md5 = MD5.Create();
int offset = 0;

// For each block:
offset += md5.TransformBlock(block, 0, block.Length, block, 0);

// For last block:
md5.TransformFinalBlock(block, 0, block.Length);

// Get the has code
byte[] hash = md5.Hash;

注:(少なくともMD5プロバイダーでは)すべてのブロックをTransformBlockに送信してから、空のブロックをTransformFinalBlockに送信してプロセスを終了します。

48
Guffa

私は上記の答えが好きですが、完全を期すために、そしてより一般的な解決策として、CryptoStreamクラスを参照してください。すでにストリームを処理している場合は、ストリームをCryptoStreamでラップし、HashAlgorithmICryptoTransformパラメーターとして渡すのは簡単です。

var file = new FileStream("foo.txt", FileMode.Open, FileAccess.Write);
var md5 = MD5.Create();
var cs = new CryptoStream(file, md5, CryptoStreamMode.Write);
while (notDoneYet)
{
    buffer = Get32MB();
    cs.Write(buffer, 0, buffer.Length);
}
System.Console.WriteLine(BitConverter.ToString(md5.Hash));

ハッシュを取得する前にストリームを閉じる必要がある場合があります(したがって、HashAlgorithmはそれが完了したことを認識します)。

49
pomeroy

このサンプルに示すように、TransformBlock/TransformFinalBlockを使用できるようです: 大きなファイルをハッシュするときに進行状況の更新を表示する

5
Rubens Farias

同様のことをしなければなりませんでしたが、ファイルを非同期で読み取りたいと思いました。 TransformBlockとTransformFinalBlockを使用していて、Azureと一致する回答が得られるので、正しいと思います。

private static async Task<string> CalculateMD5Async(string fullFileName)
{
  var block = ArrayPool<byte>.Shared.Rent(8192);
  try
  {
     using (var md5 = MD5.Create())
     {
         using (var stream = new FileStream(fullFileName, FileMode.Open, FileAccess.Read, FileShare.Read, 8192, true))
         {
            int length;
            while ((length = await stream.ReadAsync(block, 0, block.Length).ConfigureAwait(false)) > 0)
            {
               md5.TransformBlock(block, 0, length, null, 0);
            }
            md5.TransformFinalBlock(block, 0, 0);
         }
         var hash = md5.Hash;
         return Convert.ToBase64String(hash);
      }
   }
   finally
   {
      ArrayPool<byte>.Shared.Return(block);
   }
}
3
Nick Randell

ハッシュアルゴリズムはこの状況を処理することが期待されており、通常は次の3つの関数で実装されます。

hash_init()-リソースを割り当て、ハッシュを開始するために呼び出されます。
hash_update()-新しいデータが到着すると呼び出されます。
hash_final()-計算を完了してリソースを解放します。

http://www.openssl.org/docs/crypto/md5.html または http://www.openssl.org/docs/crypto/sha.html を見てください= Cの優れた標準的な例。あなたのプラットフォームにも同様のライブラリがあると確信しています。

3
Adam Liss