Использование CryptHashData для очень большого ввода - PullRequest
0 голосов
/ 22 декабря 2011

Я пытаюсь хэшировать MD5 предоставленные пользователем данные (файл), используя функции Crypto в AdvApi32. Все хорошо, если только файл не очень большой (сотни МБ или больше), и в этом случае я в итоге получаю исключение 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 голосов
/ 28 апреля 2013

Я думаю, что проблема в том, что, поскольку вы читаете данные в блоках по 4096 байт - когда данные не кратны 4096, вы в конечном итоге включаете нежелательные конечные 0 или, возможно, значения мусора. Попробуйте bs.Read(1) вместо bs.Read(4096) в цикле: Loop Until bs.EOF, чтобы проверить, рассчитывается ли сейчас правильный хеш. В случае успеха настройте цикл так, чтобы он обрабатывал оставшиеся (% 4096) байты отдельно.

0 голосов
/ 28 апреля 2013

Вы проверяли этот пример MSDN на C ++? Очень похожий ответ на ваш вопрос.

...