【问题标题】:Is it possible to copy a .NET HashAlgorithm (for repeated incremental hash results)?是否可以复制 .NET HashAlgorithm(用于重复增量哈希结果)?
【发布时间】:2014-11-25 06:00:22
【问题描述】:

我有以下用例:

  • 从文件中读取 n 个字节
  • 计算这 n 个字节的 (MD5) 哈希
  • 从文件中读取接下来的 m 个字节
  • 为文件计算 (MD5) 哈希,最多 n+m 个字节

增量散列文件不是问题,just call TransformBlock and TransformFinalBlock

问题是我需要多个共享其开始字节的数据散列,但是在我调用TransformFinalBlock 来读取第一个n 字节的Hash 之后,我无法继续使用同一个对象进行散列并且需要一个新的。

搜索问题时,我看到PythonOpenSSL 都可以选择复制散列对象来达到这个目的:

hash.copy()

返回哈希对象的副本(“克隆”)。这可用于有效计算共享公共初始子字符串的字符串摘要

 

EVP_MD_CTX_copy_ex() 可用于从 进出。 如果要散列大量数据,这很有用 仅在最后几个字节不同。 out 必须被初始化 在调用这个函数之前。

尽我所能搜索,我找不到任何与股票 C# HashAlgorithm 可以让我有效地 Clone() == 复制这样的对象之前调用它的 TransformFinalBlock 方法-- 然后继续使用克隆对其余数据进行哈希处理。

我发现了一个C# reference implementation for MD5,它可以很容易地适应支持克隆(*),但我更喜欢使用现有的东西,而不是在代码库中引入这样的东西。

(*) 事实上,据我所知,any 哈希算法(与加密/解密相反)我费心检查是可以复制的,因为这种算法的所有状态都是一种摘要形式。

那么我在这里遗漏了什么还是标准 C#/.NET 接口实际上没有提供复制哈希对象的方法?


另一个数据点:

微软自己的原生 API 用于 crypto services 有一个函数 CryptDuplicateHash,其文档引用:

CryptDuplicateHash 函数可用于创建单独的哈希 以相同内容开头的两个不同内容。

自 Windows XP 以来一直存在。 :-|


请注意。 MD5:用例对密码不敏感。只是可靠的文件校验和。

【问题讨论】:

  • 一个问题是它不能轻易克隆,一些类可能使用本机资源或调用带有句柄的专用硬件。那些类型的类不会那么容易克隆。
  • @Scott - 谢谢。是的,我想有些课程可能会。尽管如此,那些的,比如MD5,应该真的是可克隆的。所以没办法?
  • 似乎你甚至想要一个浅拷贝,你必须迭代整个东西并自己创建它。
  • @MartinBa 能否使用 TransformFinalBlock 的返回值(System.Byte[] 一个数组,该数组是输入的哈希部分的副本)手动创建副本?
  • @MartinBa 实际上MD5CngMD5CryptoServiceProvider 都调用本机代码。如果 .NET 提供了一个MD5Managed 类(类似于AesManagedRijndaelManaged),它在托管代码中完全实现了该算法,它可能很容易,但你必须去第三方来获得它(比如指向您提供的实施)

标签: c# clone md5 hashalgorithm


【解决方案1】:

我意识到这并不完全符合您的要求,但如果这与您尝试解决的问题相匹配,那么它是一种替代方法,可以为您提供相同的保证和类似的流媒体性能特征。我过去曾将其用于服务器到服务器的文件传输协议,其中发送方/接收方并不总是可用/可靠。当然,我可以控制电线两侧的代码,我意识到你可能无法控制。在这种情况下,请忽略;-)

我的方法是设置 1 个处理整个文件的 HashAlgorithm 和另一个用于散列固定大小的文件块的方法——不是滚动散列(避免你的问题),而是独立的散列。所以想象一个 1034MB (1 GB + 10 MB) 的文件在逻辑上分成 32MB 的块。发送方加载文件,同时在文件级和块级 HashAlgorithm 上调用 TransformBlock。当它到达 32MB 的末尾时,它在块级别调用 TransformFinalBlock,记录该块的哈希,并为下一个块重置/创建一个新的 HashAlgorithm。当它到达文件末尾时,它在文件和块级散列器上调用 TransformFinalBlock。现在,发送者有了一个传输“计划”,包括文件名、文件大小、文件哈希以及每个块的偏移量、长度和哈希。

它将计划发送给接收方,接收方要么为新文件分配空间(文件长度 % 块大小告诉它最后一个块小于 32MB),要么打开现有文件。如果文件已经存在,它会运行相同的算法来计算相同大小块的哈希值。与计划的任何不匹配都会导致它仅向发送者询问这些块(这将解释尚未传输的块/全 0 和损坏的块)。它这样做(验证,请求块)循环工作,直到没有任何东西可以请求。然后它根据计划检查文件级哈希。如果文件级散列无效但块级散列全部有效,则可能意味着散列冲突或 RAM 损坏(两者都极为罕见……我使用了 SHA-512)。这允许接收器从不完整的块或损坏的块中恢复,最坏的情况是不得不再次下载 1 个坏块,这可以通过调整块大小来抵消。

【讨论】:

  • 赞赏。如果性能不相关(或与文件传输速度不相关),同时运行两个哈希肯定是一个解决方案。
  • @MartinBa 您可以调整他的解决方案,使用分块哈希,但在下一个块的输入中,您连接前一个块的输出。当您想“复制”哈希时,您需要做的就是在另一个文件中的相同偏移量处重新启动,并使用第一个文件中前一个块的输出为其播种。基本上做一个与CBC Encryption Mode类似的算法,但连接输入而不是异或。
  • 开销还不错。我一次将文件 32kb/64kb 字节读入缓冲区。两个实例都处理相同的 byte[] 缓冲区,因此两次散列不会导致磁盘 I/O 损失。 I/O 是大部分速度损失的来源。
【解决方案2】:

叹息

现有的 .NET 库不允许这样做。伤心。无论如何,有几个选择:

  • MD5Managed pure .NET(“默认”MD5 RSA 许可证)
  • ClonableHash 通过 PInvoke 包装 MS Crypto API(可能需要一些工作从 Org.Mentalis 命名空间中提取它,但许可是许可的)

例如,也可以在 C++/CLI 包装器中包装 C++ implementation - 初步测试表明这似乎比普通的 .NET 库要快得多,但不要相信我的话。


因为,我自己也编写/改编了一个基于 C++ 的解决方案:https://github.com/bilbothebaggins/md5cpp

它还没有投入生产,因为需求发生了变化,但这是一个很好的练习,我认为它工作得很好。 (除了它不是纯 C# 实现。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-09
    • 1970-01-01
    • 1970-01-01
    • 2013-04-27
    • 2011-01-12
    • 1970-01-01
    • 2015-03-15
    相关资源
    最近更新 更多