ファイルのMD5チェックサムを計算する


334

iTextSharpを使用してPDFファイルからテキストを読み取ります。ただし、PDFファイルには画像しか含まれていないため、テキストを抽出できない場合があります。同じPDFファイルを毎日ダウンロードしていますが、PDFが変更されていないか確認したいと思います。テキストと変更日を取得できない場合、MD5チェックサムはファイルが変更されたかどうかを確認する最も信頼できる方法ですか?

もしそうなら、私は暗号化の経験があまりないので、いくつかのコードサンプルがありがたいです。


回答:


773

System.Security.Cryptography.MD5を使用すると、非常に簡単です。

using (var md5 = MD5.Create())
{
    using (var stream = File.OpenRead(filename))
    {
        return md5.ComputeHash(stream);
    }
}

実際に使用されているMD5実装は破棄する必要がないと思いますが、おそらくそれでも破棄するでしょう)。

後で結果を比較する方法はあなた次第です。たとえば、バイト配列をbase64に変換したり、バイトを直接比較したりできます。(配列はをオーバーライドしないことに注意してくださいEquals。base64を使用する方が簡単ですが、ハッシュの比較だけに関心がある場合は少し効率が悪くなります。)

ハッシュを文字列として表す必要がある場合は、次を使用してハッシュを16進数に変換できますBitConverter

static string CalculateMD5(string filename)
{
    using (var md5 = MD5.Create())
    {
        using (var stream = File.OpenRead(filename))
        {
            var hash = md5.ComputeHash(stream);
            return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
        }
    }
}

251
「標準的な」外観のmd5が必要な場合は、次のようにできますBitConverter.ToString(md5.ComputeHash(stream)).Replace("-","").ToLower();
。return

78
MD5はSystem.Security.Cryptographyにあります-情報をより明らかにするためです。
Hans

6
@KalaJ:意図的な改ざんを見つけようとしている場合、CRC32はまったく不適切です。データ転送の失敗を特定することだけを話している場合は、問題ありません。個人的には、たぶん習慣からSHA-256を使用するでしょう:) .NETのオフハンドでのCRC32のサポートについてはわかりませんが、おそらくできるだけ早く検索できます:)
Jon Skeet

12
@aquinas .Replace("-", String.Empty)より良いアプローチだと思います。ユーザー入力とファイルハッシュを比較したときに誤った結果が得られたため、1時間のデバッグセッションを行いました。
fabwu 2017年

7
@ wuethrich44、私が抱えている問題は、コードをaquinasコメントにそのままコピー/貼り付けした場合だと思います。偶然にも同じことに気づきました。未加工のHTMLの「空」の引用符の間に、「ゼロ幅の非結合子」とUnicodeの「ゼロ幅のスペース」の2つの非表示文字があります。元のコメントに含まれていたのか、SOのせいなのかはわかりません。
Chris Simmons

66

これが私のやり方です:

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

public string checkMD5(string filename)
{
    using (var md5 = MD5.Create())
    {
        using (var stream = File.OpenRead(filename))
        {
            return Encoding.Default.GetString(md5.ComputeHash(stream));
        }
    }
}

2
より多くの人々がこのようなことをする必要があるので、私はあなたに賛成しました。
Krythic 2016年

6
usingファイルを開くと失敗する可能性が高いので、ブロックの交換が役立つと思います。フェイルアーリー/ファストアプローチは、このようなシナリオでMD5インスタンスを作成(および破棄)するために必要なリソースを節約します。また、最初の括弧を省略して、using読みやすさを失わずにインデントのレベルを保存できます。
Palec 2016年

10
これは、16バイト長の結果を、予想される32文字の16進値ではなく、16文字の文字列に変換します。
NiKiZe

3
このコードは期待される結果(想定される想定)を生成しません。@NiKiZeに同意
Nick

1
@Quibblesome、私はステートメントを使用するネストの順序が重要であるという一般的な考えを広めようとしていました。他の場所では、その違いは重要かもしれません。故障を早期に発見する習慣を身につけてみませんか?しかし、私は、この特定のスニペットでは、習慣がほとんど利益をもたらさないことに同意します。
Palec

7

私はこの質問がすでに回答されていることを知っていますが、これは私が使用するものです:

using (FileStream fStream = File.OpenRead(filename)) {
    return GetHash<MD5>(fStream)
}

GetHashの場所:

public static String GetHash<T>(Stream stream) where T : HashAlgorithm {
    StringBuilder sb = new StringBuilder();

    MethodInfo create = typeof(T).GetMethod("Create", new Type[] {});
    using (T crypt = (T) create.Invoke(null, null)) {
        byte[] hashBytes = crypt.ComputeHash(stream);
        foreach (byte bt in hashBytes) {
            sb.Append(bt.ToString("x2"));
        }
    }
    return sb.ToString();
}

おそらく最善の方法ではありませんが、便利な場合があります。


GetHash関数に小さな変更を加えました。私はそれを拡張メソッドに変え、リフレクションコードを削除しました。
レスリーマーシャル

3
public static String GetHash<T>(this Stream stream) where T : HashAlgorithm, new() { StringBuilder sb = new StringBuilder(); using (T crypt = new T()) { byte[] hashBytes = crypt.ComputeHash(stream); foreach (byte bt in hashBytes) { sb.Append(bt.ToString("x2")); } } return sb.ToString(); }
レスリーマーシャル

これは実際に機能しました...ありがとうございます!私は予想以上に通常の32文字のmd5文字列を生成する結果をオンラインで探すのにはるかに長い時間を費やしました。これは私が好むより少し複雑ですが、確実に機能します。
トラブルサム

1
@LeslieMarshallを拡張メソッドとして使用する場合は、ストリームの位置を終了位置に残すのではなく、リセットする必要があります
MikeT

3

これが私が見つけた少しシンプルなバージョンです。ファイル全体を一度に読み取り、1つのusingディレクティブのみが必要です。

byte[] ComputeHash(string filePath)
{
    using (var md5 = MD5.Create())
    {
        return md5.ComputeHash(File.ReadAllBytes(filePath));
    }
}

50
使用の欠点はReadAllBytes、ファイル全体を単一の配列にロードすることです。これは、2 GiBを超えるファイルではまったく機能せず、中サイズのファイルでもGCに大きな負荷をかけます。ジョンの答えは少しだけ複雑ですが、これらの問題に悩まされることはありません。だから私はあなたの答えより彼の答えを好む。
CodesInChaos 2014

1
using最初の波括弧using (var md5 = MD5.Create()) using (var stream = File.OpenRead(filename))を付けずにsを続けて配置すると、不要なインデントなしで行ごとに使用できます。
NiKiZe

3
@NiKiZeプログラム全体を1行で記述して、すべてのインデントをなくすことができます。変数名としてXYZを使用することもできます!他人にとってのメリットは何ですか?
デレクジョンソン

@DerekJohnson私が言おうとしていたことは、おそらく「そして単一のusingディレクティブのみを必要とする」ということでした。すべてをメモリに読み込むのは、本当に良い理由ではありませんでした。より効果的なアプローチは、データをにストリーミングすることComputeHashです。可能なusing場合にのみ使用する必要がありますが、余分なレベルのインデントを避けたいかどうかは完全に理解できます。
NiKiZe 2017

3

私はパーティーに遅れるが、実際にソリューションを実装する前にテストを実行したことを知っています。

組み込みのMD5クラスとmd5sum.exeに対してもテストを行いました。私の場合、組み込みのクラスは13秒かかりましたが、実行ごとにmd5sum.exeが約16〜18秒かかりました。

    DateTime current = DateTime.Now;
    string file = @"C:\text.iso";//It's 2.5 Gb file
    string output;
    using (var md5 = MD5.Create())
    {
        using (var stream = File.OpenRead(file))
        {
            byte[] checksum = md5.ComputeHash(stream);
            output = BitConverter.ToString(checksum).Replace("-", String.Empty).ToLower();
            Console.WriteLine("Total seconds : " + (DateTime.Now - current).TotalSeconds.ToString() + " " + output);
        }
    }

2

MD5を計算してAzure BLOBのMD5と一致するかどうかを確認する必要がある場合は、このSOの質問と回答が役立つ可能性があります。AzureにアップロードされたBLOBのMD5ハッシュがローカルコンピューター上の同じファイルと一致しません


答えが良くないと思うなら、反対投票で結構です。ただし、反対投票の理由を説明するコメントを残すことは、時間の経過とともに回答を改善するのに役立ちます。回答を改善するための提案を含むコメントを残すことで、スタックオーバーフローへの貢献が向上します。ありがとう!
Manfred
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.