Hitung checksum MD5 untuk file

334

Saya menggunakan iTextSharp untuk membaca teks dari file PDF. Namun, ada kalanya saya tidak dapat mengekstraksi teks, karena file PDF hanya berisi gambar. Saya mengunduh file PDF yang sama setiap hari, dan saya ingin melihat apakah PDFnya sudah dimodifikasi. Jika tanggal teks dan modifikasi tidak dapat diperoleh, apakah MD5 checksum cara paling andal untuk mengetahui apakah file telah berubah?

Jika ya, beberapa contoh kode akan dihargai, karena saya tidak punya banyak pengalaman dengan kriptografi.

bangkrut
sumber

Jawaban:

773

Ini sangat sederhana menggunakan System.Security.Cryptography.MD5 :

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

(Saya percaya bahwa sebenarnya implementasi MD5 yang digunakan tidak perlu dibuang, tapi saya mungkin masih akan melakukannya.)

Bagaimana Anda membandingkan hasilnya setelah itu terserah Anda; Anda dapat mengkonversi array byte ke base64 misalnya, atau membandingkan byte secara langsung. (Perlu diketahui bahwa array tidak mengesampingkan Equals. Menggunakan base64 lebih mudah untuk dilakukan dengan benar, tetapi sedikit kurang efisien jika Anda benar-benar hanya tertarik membandingkan hash.)

Jika Anda perlu merepresentasikan hash sebagai string, Anda bisa mengubahnya menjadi hex menggunakan 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();
        }
    }
}
Jon Skeet
sumber
251
Jika Anda ingin "standar" mencari md5, Anda dapat melakukan: returnBitConverter.ToString(md5.ComputeHash(stream)).Replace("-","").ToLower();
aquinas
78
MD5 ada di System.Security.Cryptography - hanya untuk memunculkan informasi lebih lanjut.
Hans
6
@ KalJ: Jika Anda mencoba untuk menemukan perusakan yang disengaja, maka CRC32 sepenuhnya tidak pantas. Jika Anda hanya berbicara tentang menemukan kegagalan transfer data, tidak apa-apa. Secara pribadi saya mungkin akan menggunakan SHA-256 hanya karena kebiasaan :) Saya tidak tahu tentang dukungan untuk CRC32 di. NET begitu saja, tetapi Anda mungkin dapat mencarinya secepat mungkin :)
Jon Skeet
12
@ aquinas saya pikir .Replace("-", String.Empty)pendekatan yang lebih baik. Saya melewati sesi debug satu jam karena saya mendapatkan hasil yang salah ketika membandingkan input pengguna ke hash file.
fabwu
7
@ wuethrich44, saya pikir masalah yang Anda hadapi adalah jika Anda menyalin / menempelkan kode dalam aquinas komentar kata demi kata; Saya kebetulan memperhatikan hal yang sama. Ada dua karakter yang tidak terlihat - "zero-width non-joiner" dan Unicode "zero width space" - antara tanda kutip "kosong" di HTML mentah. Saya tidak tahu apakah itu dalam komentar asli atau jika SO yang harus disalahkan di sini.
Chris Simmons
66

Beginilah cara saya melakukannya:

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));
        }
    }
}
BoliBerry
sumber
2
Saya membesarkan hati Anda karena lebih banyak orang perlu melakukan hal-hal seperti ini.
Krythic
6
Saya pikir bertukar usingblok akan berguna, karena membuka file lebih mungkin akan gagal. Pendekatan awal / cepat yang gagal menghemat sumber daya yang diperlukan untuk membuat (dan menghancurkan) instance MD5 dalam skenario seperti itu. Anda juga dapat menghilangkan kawat gigi yang pertama usingdan menyimpan tingkat indentasi tanpa kehilangan keterbacaan.
Palec
10
Ini mengubah hasil 16 byte panjang menjadi string 16 karakter, bukan nilai hex 32 karakter yang diharapkan.
NiKiZe
3
Kode ini tidak menghasilkan hasil yang diharapkan (asumsi yang diharapkan). Setuju dengan @NiKiZe
Nick
1
@ Quibblesome, saya hanya mencoba untuk mempromosikan gagasan umum bahwa urutan bersarang menggunakan pernyataan penting. Di tempat lain, perbedaannya mungkin signifikan. Mengapa tidak mempraktikkan kebiasaan mendeteksi kegagalan sejak dini? Namun, saya setuju bahwa dalam cuplikan khusus ini, kebiasaan itu hampir tidak bermanfaat.
Palec
7

Saya tahu pertanyaan ini sudah dijawab, tetapi inilah yang saya gunakan:

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

Di mana 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();
}

Mungkin bukan cara terbaik, tetapi bisa berguna.

Badaro Jr.
sumber
Saya telah membuat sedikit perubahan pada fungsi GetHash Anda. Saya telah mengubahnya menjadi metode ekstensi dan menghapus kode refleksi.
Leslie Marshall
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(); }
Leslie Marshall
Ini sebenarnya berhasil .... terima kasih !. Saya menghabiskan banyak waktu untuk mencari online untuk hasil yang akan menghasilkan string 32 char md5 normal dari yang saya harapkan. Ini sedikit lebih rumit yang saya inginkan tetapi pasti berhasil.
Troublesum
1
@LeslieMarshall jika Anda akan menggunakannya sebagai metode ekstensi maka Anda harus mengatur ulang lokasi aliran daripada membiarkannya pada posisi akhir
MikeT
3

Ini adalah versi yang sedikit lebih sederhana yang saya temukan. Bunyinya seluruh file dalam sekali jalan dan hanya membutuhkan usingarahan tunggal .

byte[] ComputeHash(string filePath)
{
    using (var md5 = MD5.Create())
    {
        return md5.ComputeHash(File.ReadAllBytes(filePath));
    }
}
Ashley Davis
sumber
50
Kelemahan menggunakan ReadAllBytesadalah bahwa memuat seluruh file ke dalam satu array. Itu tidak berfungsi sama sekali untuk file yang lebih besar dari 2 GiB dan memberikan banyak tekanan pada GC bahkan untuk file berukuran sedang. Jawaban Jon hanya sedikit lebih kompleks, tetapi tidak menderita dari masalah ini. Jadi saya lebih suka jawabannya daripada jawaban Anda.
CodesInChaos
1
Masukkan usings setelah satu sama lain tanpa kurung kurawal pertama using (var md5 = MD5.Create()) using (var stream = File.OpenRead(filename))memberi Anda satu menggunakan per baris tanpa lekukan yang tidak perlu.
NiKiZe
3
@NiKiZe Anda dapat meletakkan seluruh program pada satu baris dan menghilangkan SEMUA indentasi. Anda bahkan dapat menggunakan XYZ sebagai nama variabel! Apa manfaatnya bagi orang lain?
Derek Johnson
@DerekJohnson poin yang saya coba buat adalah mungkin itu "dan hanya membutuhkan satu usingarahan." sebenarnya bukan alasan yang bagus untuk membaca semuanya dalam ingatan. Pendekatan yang lebih efektif adalah mengalirkan data ke dalam ComputeHash, dan jika mungkin usinghanya boleh digunakan, tapi saya benar-benar bisa mengerti jika Anda ingin menghindari tingkat lekukan ekstra.
NiKiZe
3

Saya tahu bahwa saya terlambat ke pesta tetapi melakukan tes sebelum benar-benar menerapkan solusi.

Saya melakukan tes terhadap kelas MD5 inbuilt dan juga md5sum.exe . Dalam kasus saya kelas inbuilt mengambil 13 detik di mana md5sum.exe juga sekitar 16-18 detik dalam setiap proses.

    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);
        }
    }
Romil Kumar Jain
sumber
2

Dan jika Anda perlu menghitung MD5 untuk melihat apakah cocok dengan MD5 dari gumpalan Azure, maka pertanyaan dan jawaban SO ini mungkin bisa membantu: G hash MD5 yang diunggah di Azure tidak cocok dengan file yang sama pada mesin lokal

Manfred
sumber
Jika Anda berpikir bahwa jawabannya tidak bagus, maka downvoting baik-baik saja. Namun, meninggalkan komentar yang menjelaskan alasan downvoate akan membantu meningkatkan jawaban seiring waktu. Dengan memberikan komentar dengan saran untuk meningkatkan jawaban, Anda dapat berkontribusi lebih baik ke Stack Overflow. Terima kasih!
Manfred