为文件夹创建哈希

Igo*_*aka 16 c# security

我需要为文件夹创建哈希,其中包含一些文件.我已经为每个文件完成了这个任务,但我正在搜索为文件夹中的所有文件创建一个哈希的方法.任何想法如何做到这一点?

(当然我可以为每个文件创建哈希并将其连接到一些大哈希,但这不是我喜欢的方式)

提前致谢.

Dun*_*unc 30

这将散列所有文件(相对)路径和内容,并正确处理文件排序.

它很快 - 就像一个4MB目录的30ms.

using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Linq;

...

public static string CreateMd5ForFolder(string path)
{
    // assuming you want to include nested folders
    var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
                         .OrderBy(p => p).ToList();

    MD5 md5 = MD5.Create();

    for(int i = 0; i < files.Count; i++)
    {
        string file = files[i];

        // hash path
        string relativePath = file.Substring(path.Length + 1);
        byte[] pathBytes = Encoding.UTF8.GetBytes(relativePath.ToLower());
        md5.TransformBlock(pathBytes, 0, pathBytes.Length, pathBytes, 0);

        // hash contents
        byte[] contentBytes = File.ReadAllBytes(file);
        if (i == files.Count - 1)
            md5.TransformFinalBlock(contentBytes, 0, contentBytes.Length);
        else
            md5.TransformBlock(contentBytes, 0, contentBytes.Length, contentBytes, 0);
    }

    return BitConverter.ToString(md5.Hash).Replace("-", "").ToLower();
}
Run Code Online (Sandbox Code Playgroud)

  • 小心搜索模式 - 应该是"\*"而不是"\*.\*" (3认同)
  • 如果您将此部署到具有强制执行FIPS兼容性的本地安全策略的服务器,请注意FIPS合规性 (2认同)

小智 13

Dunc的答案很有效; 但是,它不处理空目录.下面的代码返回空目录的MD5'd41d8cd98f00b204e9800998ecf8427e'(0长度字符流的MD5).

public static string CreateDirectoryMd5(string srcPath)
{
    var filePaths = Directory.GetFiles(srcPath, "*", SearchOption.AllDirectories).OrderBy(p => p).ToArray();

    using (var md5 = MD5.Create())
    {
        foreach (var filePath in filePaths)
        {
            // hash path
            byte[] pathBytes = Encoding.UTF8.GetBytes(filePath);
            md5.TransformBlock(pathBytes, 0, pathBytes.Length, pathBytes, 0);

            // hash contents
            byte[] contentBytes = File.ReadAllBytes(filePath);

            md5.TransformBlock(contentBytes, 0, contentBytes.Length, contentBytes, 0);
        }

        //Handles empty filePaths case
        md5.TransformFinalBlock(new byte[0], 0, 0);

        return BitConverter.ToString(md5.Hash).Replace("-", "").ToLower();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果使用此版本,则需要将`filePath`截断为相对路径以创建`pathBytes`. (2认同)

Pau*_*ane 7

创建文件的tarball,散列tarball.

> tar cf hashes *.abc
> md5sum hashes
Run Code Online (Sandbox Code Playgroud)

或者将单个文件和管道输出散列为hash命令.

> md5sum *.abc | md5sum
Run Code Online (Sandbox Code Playgroud)

编辑:上面的两种方法都不对文件进行排序,因此可能会为每次调用返回不同的哈希值,具体取决于shell如何扩展星号.


Ron*_*rby 5

这是一个使用流来避免内存和延迟问题的解决方案。

默认情况下,文件路径包含在散列中,这不仅会考虑文件中的数据,还会考虑文件系统条目本身,从而避免散列冲突。这篇文章被标记为security,所以这应该很重要。

最后,此解决方案使您可以控制散列算法、散列哪些文件以及以何种顺序进行散列。

public static class HashAlgorithmExtensions
{
    public static async Task<byte[]> ComputeHashAsync(this HashAlgorithm alg, IEnumerable<FileInfo> files, bool includePaths = true)
    {
        using (var cs = new CryptoStream(Stream.Null, alg, CryptoStreamMode.Write))
        {
            foreach (var file in files)
            {
                if (includePaths)
                {
                    var pathBytes = Encoding.UTF8.GetBytes(file.FullName);
                    cs.Write(pathBytes, 0, pathBytes.Length);
                }

                using (var fs = file.OpenRead())
                    await fs.CopyToAsync(cs);
            }

            cs.FlushFinalBlock();
        }

        return alg.Hash;
    }
}
Run Code Online (Sandbox Code Playgroud)

散列文件夹中所有文件的示例:

async Task<byte[]> HashFolder(DirectoryInfo folder, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
    using(var alg = MD5.Create())
        return await alg.ComputeHashAsync(folder.EnumerateFiles(searchPattern, searchOption));
}
Run Code Online (Sandbox Code Playgroud)

  • 此外,我建议对文件名进行排序并使用 Path.GetRelativePath(folder, file.FullName) ,这样即使文件位于不同的文件夹位置,哈希值也是稳定的。 (2认同)

aul*_*ron 1

将文件名和文件内容连接在一个大字符串中并对其进行哈希处理,或者以块的形式进行哈希处理以提高性能。

当然,您需要考虑以下几点:

  • 您需要按名称对文件进行排序,这样万一文件顺序发生变化,您就不会得到两个不同的哈希值。
  • 使用此方法,您只需考虑文件名和内容。如果文件名不重要,您可以先按内容排序,然后进行散列,如果更多属性(ctime/mtime/hidden/archived..)很重要,请将它们包含在要散列的字符串中。