2011-12-21 20 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

that msdn exampleをC++でチェックしましたか? あなたの質問に非常によく似た回答です。

0

私は4096バイトのブロックでデータを読むので、データが4096の倍数でないときは、不要な末尾の0や不要な値を含めてエンディングすることが問題だと思います。ループ内にbs.Read(4096)の代わりにbs.Read(1)を試してください。Loop Until bs.EOFが正しいハッシュが現在計算されているかどうかをテストしてください。成功した場合は、ループを調整して余り(%4096)バイトを個別に処理します。

関連する問題