计算文件的 MD5 校验和

Posted

技术标签:

【中文标题】计算文件的 MD5 校验和【英文标题】:Calculate MD5 checksum for a file 【发布时间】:2012-05-18 04:50:53 【问题描述】:

我正在使用iTextSharp 从 PDF 文件中读取文本。但是,有时我无法提取文本,因为 PDF 文件只包含图像。我每天都下载相同的PDF文件,我想看看PDF是否被修改过。如果无法获得文本和修改日期,MD5 校验和是否是判断文件是否已更改的最可靠方法?

如果是,一些代码示例将不胜感激,因为我在密码学方面没有太多经验。

【问题讨论】:

msdn.microsoft.com/en-us/library/… 【参考方案1】:

使用System.Security.Cryptography.MD5很简单:

using (var md5 = MD5.Create())

    using (var stream = File.OpenRead(filename))
    
        return md5.ComputeHash(stream);
    

(我相信实际上使用的 MD5 实现不需要被释放,但我可能仍然会这样做。)

之后如何比较结果取决于您;例如,您可以将字节数组转换为 base64,或直接比较字节。 (请注意,数组不会覆盖 Equals。使用 base64 更容易正确,但如果您真的只对比较哈希感兴趣,则效率会稍低。)

如果您需要将哈希表示为字符串,可以使用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();
        
    

【讨论】:

如果你想要“标准”的 md5,你可以这样做:return BitConverter.ToString(md5.ComputeHash(stream)).Replace("-","").ToLower(); MD5 在 System.Security.Cryptography - 只是为了显示更多信息。 @KalaJ:如果您试图发现故意篡改,那么 CRC32 是完全不合适的。如果您只是在谈论发现数据传输失败,那很好。就我个人而言,我可能只是出于习惯而使用 SHA-256 :) 我不知道 .NET 中对 CRC32 的支持,但你可以尽快搜索它 :) @aquinas 我认为.Replace("-", String.Empty) 是一种更好的方法。我经历了一个小时的调试会话,因为在将用户输入与文件哈希进行比较时得到了错误的结果。 @wuethrich44,我认为您遇到的问题是,如果您逐字复制/粘贴 aquinas 评论中的代码;我碰巧注意到了同样的事情。在原始 html 中的“空”引号之间有两个不可见的字符——一个“零宽度非连接符”和一个 Unicode“零宽度空格”。我不知道它是否在原始评论中,或者是否应该归咎于此。【参考方案2】:

这就是我的做法:

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));
        
    

【讨论】:

我支持你是因为更多的人需要做这样的事情。 我认为交换 using 块会很有用,因为打开文件更有可能会失败。早期失败/快速失败的方法可以为您节省在这种情况下创建(和销毁)MD5 实例所需的资源。您也可以省略第一个 using 的大括号并保存一定程度的缩进而不会失去可读性。 这会将 16 字节长的结果转换为 16 个字符的字符串,而不是预期的 32 个字符的十六进制值。 这段代码没有产生预期的结果(假设的预期)。同意@NiKiZe 与 Jon Skeet 对 BitConverter 的回答不同,Encoding.Default.GetString 为我返回 nonascii 字符乱码(在 Unity 中运行)。【参考方案3】:

我知道这个问题已经回答了,但这是我使用的:

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 函数做了一些小改动。我已经把它变成了一个扩展方法,并去掉了反射代码。 public static String GetHash&lt;T&gt;(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 char md5 字符串的结果。这有点复杂,我更喜欢,但它确实有效。 @LeslieMarshall 如果您打算将其用作扩展方法,那么您应该重置流位置而不是将其留在结束位置 我对@LeslieMarshall 使用(T) HashAlgorithm.Create(typeof(T).Name) 并删除new() 约束的方法有更好的运气。对于我的实现,我还对其进行了更改,因此参数为this byte[] resource,并将流放入带有using var stream = new MemoryStream(resource) 的方法中。然后你只需要告诉编译器crypt 不为空。【参考方案4】:

这是我发现的一个稍微简单的版本。它一次性读取整个文件,并且只需要一个 using 指令。

byte[] ComputeHash(string filePath)

    using (var md5 = MD5.Create())
    
        return md5.ComputeHash(File.ReadAllBytes(filePath));
    

【讨论】:

使用ReadAllBytes 的缺点是它将整个文件加载到单个数组中。对于大于 2 GiB 的文件,这根本不起作用,即使对于中等大小的文件,也会给 GC 带来很大压力。乔恩的答案只是稍微复杂一些,但不会遇到这些问题。所以我更喜欢他的回答而不是你的回答。 usings 一个接一个地放入,不带第一个花括号using (var md5 = MD5.Create()) using (var stream = File.OpenRead(filename)) 为您提供每行使用一个而没有不必要的缩进。 @NiKiZe 您可以将整个程序放在一行上并消除所有缩进。您甚至可以使用 XYZ 作为变量名!对他人有什么好处? @DerekJohnson 我试图表达的观点可能是“并且只需要一个 using 指令。”这并不是将所有内容读入内存的好理由。更有效的方法是将数据流式传输到ComputeHash,如果可能,应该只使用using,但如果你想避免额外的缩进,我完全可以理解。【参考方案5】:

我知道我迟到了,但在实际实施解决方案之前进行了测试。

我确实对内置的 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);
        
    

【讨论】:

【参考方案6】:

如果您需要计算 MD5 以查看它是否与 Azure blob 的 MD5 匹配,那么这个 SO 问题和答案可能会有所帮助:MD5 hash of blob uploaded on Azure doesnt match with same file on local machine

【讨论】:

如果您认为答案不是很好,那么投反对票就可以了。但是,留下评论来描述拒绝投票的原因将有助于随着时间的推移改进答案。通过留下评论以改进答案,您可以更好地为 Stack Overflow 做出贡献。谢谢!【参考方案7】:

用于动态生成的 PDF。 创建日期和修改日期总是不同的。

您必须删除它们或将它们设置为常数值。

然后生成 md5 哈希来比较哈希。

您可以使用PDFStamper 删除或更新日期。

【讨论】:

以上是关于计算文件的 MD5 校验和的主要内容,如果未能解决你的问题,请参考以下文章

Ubuntu镜像的MD5校验

校验MD5 是啥 意思?

.md5文件怎么用的

Md5是啥?MD5怎么校验?Md5校验工具怎么用

2.7校验和与核实

Python计算校验文件的MD5SHA1SHA256和CRC32