2011-12-21 7 views
0

AdvApi32의 Crypto 함수를 사용하여 MD5 해시 사용자 제공 데이터 (파일)를 만들려고합니다. 파일이 매우 크거나 (수백 MB 이상) 그렇지 않으면 결국 OutOfMemory 예외가 발생합니다.매우 큰 입력에서 CryptHashData 사용

나는 한 번에 같은 HashObject을 처리하고 (예를 들어) 4096 바이트 만 처리하여 CryptHashData을 반복적으로 호출하는 것이 해결책이라고 생각했습니다.

이것은 정상적으로 작동하지만 반환 된 해시가 잘못되었습니다.

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 

여기서 내가 뭘 잘못하고 있니? 내가 얻는 출력은 일관성이 있지만 잘못되었습니다.

+0

파일 데이터 길이가 4096 바이트의 정확한 배수가 아닌 경우 코드가 올바르게 처리되지 않는다고 생각합니다. –

+0

@ David Schwartz Hmm. 좋은 지적! 코드를 약간 수정 해 보겠습니다. –

+0

@David Schwartz는 슬프게도 그것을 고치지 않는 것으로 보입니다. –

답변

0

데이터가 4096 바이트의 블록으로 읽혀지기 때문에 데이터가 4096의 배수가 아니므로 원하지 않는 후행 0 또는 가능한 가비지 값을 포함하여 늘어나는 문제가 있다고 생각합니다. 루프에서 bs.Read(4096) 대신 bs.Read(1)을 시도해보십시오. 올바른 해시가 지금 계산 중인지 테스트하려면 Loop Until bs.EOF이 필요합니다. 루프가 성공적으로 완료되면 나머지 (% 4096) 바이트를 별도로 처리합니다.