【问题标题】:Get a file SHA256 Hash code and Checksum获取文件 SHA256 哈希码和校验和
【发布时间】:2016-11-23 06:47:34
【问题描述】:

之前我向question 询问了有关组合 SHA1+MD5 的问题,但在那之后我了解到计算 SHA1 然后 MD5 的 lagrge 文件并不比 SHA256 快。 在我的例子中,一个 4.6 GB 的文件大约需要 10 分钟,在 Linux 系统中使用默认实现 SHA256 和 (C# MONO)。

public static string GetChecksum(string file)
{
    using (FileStream stream = File.OpenRead(file))
    {
        var sha = new SHA256Managed();
        byte[] checksum = sha.ComputeHash(stream);
        return BitConverter.ToString(checksum).Replace("-", String.Empty);
    }
}

然后我阅读了this topic,并根据他们所说的以某种方式更改了我的代码:

public static string GetChecksumBuffered(Stream stream)
{
    using (var bufferedStream = new BufferedStream(stream, 1024 * 32))
    {
        var sha = new SHA256Managed();
        byte[] checksum = sha.ComputeHash(bufferedStream);
        return BitConverter.ToString(checksum).Replace("-", String.Empty);
    }
}

但它没有这样的感情,大约需要9分钟。

然后我尝试在 Linux 中通过sha256sum 命令测试我的文件以获得相同的文件,大约需要 28 秒,上面的代码和 Linux 命令都给出相同的结果!

有人建议我阅读哈希码和校验和之间的差异,我联系了 this topic 来解释差异。

我的问题是:

  1. 是什么原因导致上述代码与 Linux sha256sum 在时间上如此不同?

  2. 上面的代码是做什么的? (我的意思是哈希码计算还是校验和计算?因为如果你在C#中搜索给出文件的哈希码和文件的校验和,它们都会到达上面的代码。)

  3. 即使 SHA256 是抗碰撞的,是否有任何针对 sha256sum 的动机攻击?

  4. 如何使我的实现与 C# 中的 sha256sum 一样快?

【问题讨论】:

  • 您是否有理由无法使用 Process 从代码中调用 sha256sum
  • @NateDiamond 是的!首先,该程序必须在 Windows 和 linux 上运行。其次,正如我在问题中提到的,我不知道校验和是否足够安全。(或哈希码)
  • 这应该完全被从磁盘读取文件的成本所限制。 9 分钟并非不可想象,但您需要一台廉价的笔记本电脑,配备糟糕的主轴驱动器且内存不足。记录您使用的内容。
  • @HansPassant 其实我现在想知道文件的哈希码和文件的校验和有什么不同吗?

标签: c# mono hashcode checksum sha256


【解决方案1】:
  1. 我的最佳猜测是,File.Read 操作的 Mono 实现中存在一些额外的缓冲。最近研究了一个大文件的校验和,在一台规格不错的 Windows 机器上,如果一切运行顺利,您应该预计每 Gb 大约 6 秒。

    奇怪的是,在不止一项基准测试中报告说 SHA-512 明显快于 SHA-256(参见下面的 3)。另一种可能性是问题不在于分配数据,而在于一旦读取就处理字节。您也许可以在单个数组上使用TransformBlock(和TransformFinalBlock),而不是一口气读取流——我不知道这是否可行,但值得研究。

  2. 哈希码和校验和之间的区别是(几乎)语义。它们都计算出一个较短的“魔术”数字,该数字对输入中的数据相当独特,但如果您有 4.6GB 的输入和 64B 的输出,那么“相当”就会受到一定的限制。

    • 校验和是不安全的,通过一些工作,您可以从足够多的输出中找出输入,从输出到输入逆向工作,以及做各种不安全的事情。
    • 加密哈希需要更长的时间来计算,但仅更改输入中的一位就会从根本上改变输出,并且对于良好的哈希(例如 SHA-512),没有已知的方法可以从输出返回到输入。
  3. MD5 是可破坏的:如果需要,您可以在 PC 上制造输入以产生任何给定的输出。 SHA-256 (可能)仍然是安全的,但不会在几年内实现——如果您的项目的生命周期以几十年来衡量,那么假设您需要对其进行更改。 SHA-512 没有已知的攻击,可能在很长一段时间内都不会,而且由于它比 SHA-256 更快,我还是推荐它。基准测试表明,计算 SHA-512 所需的时间是 MD5 的 3 倍左右,所以如果您的速度问题可以得到解决,那就是要走的路。

  4. 不知道,除了上面提到的那些。你做得对。

如需简单阅读,请参阅Crypto.SE: SHA51 is faster than SHA256?

针对评论中的问题进行编辑

校验和的目的是让您检查文件在您最初编写它的时间和您开始使用它的时间之间是否发生了变化。它通过产生一个小值(在 SHA512 的情况下为 512 位)来实现这一点,其中原始文件的每一位都至少对输出值有所贡献。哈希码的目的是相同的,此外,其他任何人都很难通过对文件进行精心管理的更改来获得相同的输出值。

前提是,如果校验和在开始时和检查时相同,则文件相同,如果它们不同,则文件肯定已更改。您在上面所做的是通过一种算法将整个文件提供给文件,该算法会滚动、折叠和旋转它读取的位以产生较小的值。

例如:在我当前编写的应用程序中,我需要知道任何大小的文件的某些部分是否已更改。我将文件分成 16K 块,获取每个块的 SHA-512 哈希,并将其存储在另一个驱动器上的单独数据库中。当我来查看文件是否已更改时,我会复制每个块的哈希并将其与原始文件进行比较。由于我使用的是 SHA-512,因此更改文件具有相同散列的可能性非常小,因此我可以确信检测到 100 GB 数据中的更改,同时仅在我的数据库中存储几 MB 散列。我在获取哈希的同时复制文件,这个过程完全是磁盘绑定的;将文件传输到 U 盘大约需要 5 分钟,其中 10 秒可能与哈希有关。

存储哈希的磁盘空间不足是我无法在帖子中解决的问题 - 购买 U 盘?

【讨论】:

  • 好吧,sha256 比 sha512 慢真是太神奇了!好吧,我还有一个问题。我上面提到的代码是做什么的?这可能是个笑话,但当我搜索“获取文件校验和”和“获取文件哈希”时,我找不到任何不同之处。他们都给出相同的结果。似乎人们不知道他们到底在做什么:)。 (像我一样!)
  • 另一件事是我不能为 sha512 存储 128 字节!那里有大量文件,但我没有足够的音量。
【解决方案2】:
public string SHA256CheckSum(string filePath)
{
    using (SHA256 SHA256 = SHA256Managed.Create())
    {
        using (FileStream fileStream = File.OpenRead(filePath))
            return Convert.ToBase64String(SHA256.ComputeHash(fileStream));
    }
}

【讨论】:

  • Convert.ToBase64String 是错误的。你应该使用BitConverter.ToString(SHA256.ComputeHash(fileStream)).Replace("-", "").ToLowerInvariant(); 否则哈希会出错。
  • @Mariot 请更新您的答案以使用BitConverter.ToString() 而不是Convert.ToBase64String()。我不想对您的答案投反对票,因为它大部分是正确的。
【解决方案3】:

聚会迟到了,但由于没有一个答案提到它,我想指出:

SHA256ManagedSystem.Security.Cryptography.HashAlgorithm 类的实现,所有与读取操作相关的功能都在继承的代码中处理。

HashAlgorithm.ComputeHash(Stream) 使用固定的 4096 字节缓冲区从流中读取数据。因此,使用 BufferedStream 进行此调用不会有太大区别。

HashAlgorithm.ComputeHash(byte[]) 对整个字节数组进行操作,但它会在每次调用后重置内部状态,因此不能用于增量散列缓冲流。

您最好的选择是使用针对您的用例进行了优化的第三方实现。

【讨论】:

    【解决方案4】:
    using (SHA256 SHA256 = SHA256Managed.Create())
                {
                    using (FileStream fileStream = System.IO.File.OpenRead(filePath))
                    {
                        string result = "";
                        foreach (var hash in SHA256.ComputeHash(fileStream))
                        {
                            result += hash.ToString("x2");
                        }
    
                        return result;
                    }
                }
    

    供参考:https://www.c-sharpcorner.com/article/how-to-convert-a-byte-array-to-a-string/

    【讨论】:

    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 2010-10-02
    • 2013-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-28
    相关资源
    最近更新 更多