计算文件的MD5校验和

bro*_*oke 319 .net c# hash md5

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

如果是的话,一些代码样本会受到赞赏,因为我对密码学没有多少经验.

Jon*_*eet 728

使用System.Security.Cryptography.MD5非常简单:

using (var md5 = MD5.Create())
{
    using (var stream = File.OpenRead(filename))
    {
        return md5.ComputeHash(stream);
    }
}
Run Code Online (Sandbox Code Playgroud)

(我相信实际上使用的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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

小智 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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

小智 7

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

using (FileStream fStream = File.OpenRead(filename)) {
    return GetHash<MD5>(fStream)
}
Run Code Online (Sandbox Code Playgroud)

其中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();
}
Run Code Online (Sandbox Code Playgroud)

可能不是最好的方式,但它可以很方便.

  • `public static String GetHash <T>(此流stream)其中T:HashAlgorithm,new(){StringBuilder sb = new StringBuilder(); 使用(T crypt = new T()){byte [] hashBytes = crypt.ComputeHash(stream); foreach(hashBytes中的字节bt){sb.Append(bt.ToString("x2")); 返回sb.ToString(); }` (2认同)

Ash*_*vis 5

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

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

  • 使用 ReadAllBytes 的缺点是它将整个文件加载到单个数组中。这对于大于 2 GiB 的文件根本不起作用,即使对于中等大小的文件也会给 GC 带来很大的压力。乔恩的答案只是稍微复杂一些,但不会遇到这些问题。所以我更喜欢他的回答而不是你的。 (58认同)
  • @NiKiZe您可以将整个程序放在一行上并消除所有缩进。您甚至可以使用 XYZ 作为变量名!对他人有什么好处? (3认同)

Rom*_*ain 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);
        }
    }
Run Code Online (Sandbox Code Playgroud)