2014-09-30 40 views
11

我有以下用例:是否有可能复制.NET HashAlgorithm(用于重复增量散列结果)?

  • 读取n个从一个文件的字节
  • 计算(MD5)散列这些n个字节
  • 阅读来自文件
  • 计算(MD5)散列接下来的m个字节对于最多n + m个字节的文件

递增散列文件不是问题。

的问题是,我需要共享其开始字节数据的多个哈希值,但我已经叫TransformFinalBlock后读取第一n字节,我不能继续使用相同的对象散列Hash,需要一个新的。

搜索的问题,我看到这两个Python以及OpenSSL有一个选项来复制一个散列对象出于这样的目的:

hash.copy()

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

 

EVP_MD_CTX_copy_ex()可以用于复制信息从 到出消化状态。 如果要对大量数据进行散列,这很有用 仅在最后几个字节中有所不同。在调用此函数之前,必须初始化 。

搜索,因为我可以,我无法找到任何withing股票C#HashAlgorithm,让我能够有效Clone() ==复制这样的对象之前调用其TransformFinalBlock方法 - 事后继续散列其余的数据与克隆。

我发现了一个可以轻松适应克隆(*)的C# reference implementation for MD5,但强烈倾向于使用那里的东西而不是将这样的东西引入代码库。 (*)据我所知,任何散列算法(而不是加密/解密)我一直困扰检查是微不足道的,因为所有的这种算法的状态是一种形式的消化。

所以我在这里丢失了什么,或者标准的C#/ .NET接口实际上没有提供复制散列对象的方法吗?


另一个数据点:

crypto services微软自己原生API有一个函数CryptDuplicateHash,其状态的文档,报价:

的CryptDuplicateHash功能,可用于创建分开哈希 两个不同的内容,开始于相同的内容。

自从Windows XP出现以来, : - |


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

+0

它的一个问题不是一成不变的克隆,有些类可能会使用本地资源或调用具有句柄的专用硬件。这些类的类型不会很容易克隆。 – 2014-09-30 14:56:49

+0

@Scott - 谢谢。是的,我想有些班可能会。但是,那些*不*像MD5那样的应该是可以克隆的。所以没办法? – 2014-09-30 15:21:03

+0

看起来好像你想要一个浅拷贝,你将不得不迭代整个事物并自己创建它。 – 2014-09-30 16:21:28

回答

3

SIGH

股票.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#实现)。

4

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

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

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

+0

赞赏。如果性能不相关(或不相关,例如文件传输速度),则同时运行两个哈希值肯定是一种解决方案。 – 2014-10-17 20:28:25

+0

@MartinBa你可以调整他的解决方案,使用分块散列,但在输入为下一个块你输出前一个块。当你想“复制”散列时,你需要做的就是在另一个文件的同一个偏移量处重新启动,并将其与第一个文件中前一个块的输出结合起来。基本上做一个[CBC加密模式](http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29)的类似算法,但是对输入进行分级而不是对其进行异或。 – 2014-10-17 20:30:31

+1

开销不错。我一次将32kb/64kb的文件读入缓冲区。两个实例都处理相同的byte []缓冲区,所以没有磁盘I/O处理两次哈希。 I/O是速度惩罚的大部分来源。 – scottt732 2014-10-17 20:34:22