是否可以复制.NET哈希算法(用于重复的增量哈希结果)

本文关键字:哈希 结果 复制 NET 是否 算法 用于 | 更新日期: 2023-09-27 18:25:02

我有以下用例:

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

增量哈希文件不是问题,只需调用TransformBlockTransformFinalBlock即可。

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

在搜索这个问题时,我发现Python和OpenSSL都可以选择复制哈希对象来达到这个目的:

hash.copy()

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

 

EVP_MD_CTX_copy_ex()可用于从从内到外如果要对大量数据进行哈希处理,这很有用其仅在最后几个字节中不同。out必须初始化在调用此函数之前。

尽管我可能会进行搜索,但在股票C#哈希算法中找不到任何可以让我在调用其TransformFinalBlock方法之前有效地Clone()==复制这样一个对象的东西,然后继续用克隆对其余数据进行哈希。

我发现了一个MD5的C#参考实现,它可以简单地适应于支持克隆(*),但我更喜欢使用现有的东西,而不是将这样的东西引入代码库。

(*)事实上,据我所知,我不厌其烦地检查的任何哈希算法(与加密/解密相反)都是可复制的,因为这种算法的所有状态都是摘要的形式。

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


另一个数据点:

微软的自己的本地API加密服务有一个函数CryptDuplicateHash,其状态的文档,引用:

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

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


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

是否可以复制.NET哈希算法(用于重复的增量哈希结果)

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

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

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

SIGH

现有的.NET库不允许这样做。悲哀的无论如何,有几个替代方案:

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

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


自那以后,我还自己编写/改编了一个基于C++的解决方案:https://github.com/bilbothebaggins/md5cpp

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