파일에 대한 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를 원한다면 다음과 같이 할 수 있습니다 : returnBitConverter.ToString(md5.ComputeHash(stream)).Replace("-","").ToLower();
aquinas

78
MD5는 System.Security.Cryptography에 있으며 정보를 더 많이 표시합니다.
Hans

6
@KalaJ : 고의적 인 변조를 발견하려는 경우 CRC32는 전적으로 부적절합니다. 데이터 전송 실패를 발견하는 것에 대해서만 이야기하는 것이 좋습니다. 개인적으로 나는 아마 습관에서 SHA-256을 사용하십시오 : 나는 .NET 즉석하지만, 당신은 아마 빨리 내가 :) 수로 검색 할 수있는 CRC32에 대한 지원에 대해 알고하지 않습니다
존 소총

12
@ aquinas .Replace("-", String.Empty)더 나은 접근법 이라고 생각 합니다. 사용자 입력을 파일 해시와 비교할 때 잘못된 결과가 나오기 때문에 1 시간 디버그 세션을 거쳤습니다.
fabwu

7
@ wuethrich44, 당신이 겪고있는 문제는 aquinas 주석에 코드를 복사 / 붙여 넣는 것입니다. 나는 같은 것을 알아 차렸다. 원시 HTML의 "빈"따옴표 사이에는 두 개의 보이지 않는 문자 ( "폭이 0이 아닌 조인자"및 유니 코드 "폭이 0 인 공백")가 있습니다. 그것이 원래 의견에 있었는지 또는 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

6
using파일을 여는 것이 실패 할 가능성이 높기 때문에 블록을 바꾸는 것이 유용 할 것이라고 생각합니다 . 조기 / 빠른 접근 실패는 이러한 시나리오에서 MD5 인스턴스를 생성 및 파괴하는 데 필요한 리소스를 절약합니다. 또한 첫 번째 중괄호를 생략하고 using가독성을 잃지 않고 들여 쓰기 수준을 저장할 수 있습니다.
Palec

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 문자열을 생성하는 결과를 온라인에서 오랫동안 보냈다. 이것은 내가 선호하는 조금 더 복잡하지만 확실히 작동합니다.
Troublesum

1
@LeslieMarshall 확장 방법으로 사용하려면 스트림 위치를 끝 위치에 두지 말고 재설정해야합니다.
MikeT

3

여기 내가 찾은 약간 더 간단한 버전이 있습니다. 전체 파일을 한 번에 읽고 단일 using지시문 만 필요합니다 .

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

50
사용의 단점은 ReadAllBytes전체 파일을 단일 배열로로드한다는 것입니다. 2GiB보다 큰 파일에서는 전혀 작동하지 않으며 중간 크기 파일에서도 GC에 많은 부담을줍니다. Jon의 대답은 약간 더 복잡하지만 이러한 문제로 고통받지 않습니다. 그래서 나는 당신보다 그의 대답을 선호합니다.
코드 InChaos

1
using첫 번째 중괄호 using (var md5 = MD5.Create()) using (var stream = File.OpenRead(filename))없이 서로를 s에 넣으면 불필요한 들여 쓰기없이 한 줄에 하나씩 사용할 수 있습니다.
NiKiZe

3
@NiKiZe 한 줄에 전체 프로그램을 넣고 모든 들여 쓰기를 제거 할 수 있습니다. XYZ를 변수 이름으로 사용할 수도 있습니다! 다른 사람에게는 어떤 유익이 있습니까?
Derek Johnson

@DerekJohnson 내가 만들고자하는 요점은 아마도 "단일 using지시어 만 있으면된다"는 것이 었습니다 . 모든 것을 메모리로 읽어야하는 좋은 이유는 아니 었습니다. 보다 효과적인 접근 방식은 데이터를에 스트리밍하는 것이며 ComputeHash가능한 using경우에만 사용해야하지만 추가 수준의 들여 쓰기를 피하려면 완전히 이해할 수 있습니다.
NiKiZe

3

나는 파티에 늦었지만 실제로 솔루션을 구현하기 전에 테스트를 수행했다는 것을 알고 있습니다.

내장 MD5 클래스와 md5sum.exe 에 대한 테스트를 수행 했습니다 . 필자의 경우 내장 클래스는 md5sum.exe가 매 실행마다 16-18 초 정도 걸리는 13 초가 걸렸습니다.

    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.