在非常大的输入上使用CryptHashData

时间:2011-12-21 23:54:25

标签: winapi cryptoapi realbasic

我正在使用AdvApi32中的Crypto函数尝试MD5哈希用户提供的数据(文件)。除非文件非常大(数百MB或更大),否则一切都很好,在这种情况下我最终得到一个OutOfMemory异常。

我认为解决方案是使用相同的CryptHashData重复调用HashObject,并且一次只处理(例如)4096个字节。

这似乎有效,但返回的哈希值不正确。

Function HashFile(File As FolderItem) As String
  Declare Function CryptAcquireContextW Lib "AdvApi32" (ByRef provider as Integer, container as Integer, providerName as WString, _
  providerType as Integer, flags as Integer) as Boolean
  Declare Sub CryptDestroyHash Lib "AdvApi32" (hashHandle as Integer )
  Declare Function CryptCreateHash Lib "AdvApi32" (provider as Integer, algorithm as Integer, key as Integer, flags as Integer, _
  ByRef hashHandle as Integer) as Boolean
  Declare Function CryptHashData Lib "AdvApi32" (hashHandle as Integer, data as Ptr, length as Integer, flags as Integer) as Boolean
  Declare Function CryptGetHashParam Lib "AdvApi32" (hashHandle as Integer, type as Integer, value as Ptr, ByRef length as Integer, _
  flags as Integer) as Boolean

  Const HP_HASHVAL = &h0002
  Const HP_HASHSIZE = &h0004
  Const MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0"
  Const PROV_RSA_FULL = 1
  Const CRYPT_NEWKEYSET = &h00000008
  Const CALG_MD5 = &h00008003

  Dim provider As Integer
  Dim hashHandle As Integer

  If Not CryptAcquireContextW(provider, 0, MS_DEF_PROV, PROV_RSA_FULL, 0) Then
    If Not CryptAcquireContextW(provider, 0, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET) Then
      Raise New RuntimeException
    End If
  End If

  If Not CryptCreateHash(provider, CALG_MD5, 0, 0, hashHandle) Then
    Raise New RuntimeException
  End If

  Dim dataPtr As New MemoryBlock(4096)
  Dim bs As BinaryStream
  bs = bs.Open(File)
  dataPtr.StringValue(0, 4096) = bs.Read(4096)

  Do
    If CryptHashData(hashHandle, dataPtr, dataPtr.Size, 0) Then
      dataPtr = New MemoryBlock(4096)
      dataPtr.StringValue(0, 4095) = bs.Read(4096)
    End If
  Loop Until bs.EOF

  Dim size as Integer = 4
  Dim toss As New MemoryBlock(4)
  If Not CryptGetHashParam(hashHandle, HP_HASHSIZE, toss, size, 0) Then
    Raise New RuntimeException
  End If

  size = toss.UInt32Value(0)

  Dim hashValue As New MemoryBlock(size)
  If Not CryptGetHashParam(hashHandle, HP_HASHVAL, hashValue, size, 0) Then
    Raise New RuntimeException
  End If
  CryptDestroyHash(hashHandle)

  //Convert binary to hex
  Dim hexvalue As Integer
  Dim hexedInt As String
  Dim src As String = hashValue.StringValue(0, hashValue.Size)
  For i As Integer = 1 To LenB(src)
    hexvalue = AscB(MidB(src, i, 1))
    hexedInt = hexedInt + RightB("00" + Hex(hexvalue), 2)
  next

  Return LeftB(hexedInt, LenB(hexedInt))

End Function

我在这里做错了什么?我得到的输出是一致的,但错了。

2 个答案:

答案 0 :(得分:0)

您是否在C ++上查看that msdn example了吗? 对你的问题非常相似的答案。

答案 1 :(得分:0)

我认为问题在于,因为您以4096字节的块读取数据 - 当数据不是4096的倍数时,您最终会包含不需要的尾随0或可能是垃圾值。请在循环中尝试bs.Read(1)而不是bs.Read(4096)Loop Until bs.EOF以测试是否正在计算正确的哈希值。如果成功调整循环以分别处理余数(%4096)字节。