MD5 Hashing Using the CryptoAPI

From HashVB
Jump to: navigation, search

MD5 (or almost any other hashing algorithm you could possibly want to work with) is provided for you by the Windows Cryptography API. The code to use this API is as follows:

   Option Explicit
   
   Private Const ALG_CLASS_HASH As Long = (4 * 2 ^ 13)
   Private Const ALG_SID_MD5 As Long = 3
   Private Const ALG_TYPE_ANY As Long = 0
   Private Const CALG_MD5 As Long = (ALG_CLASS_HASH Or ALG_TYPE_ANY Or ALG_SID_MD5)
   Private Const CRYPT_VERIFYCONTEXT As Long = &HF0000000
   Private Const PROV_RSA_FULL As Long = 1
   
   Private Const HP_HASHSIZE As Long = &H4
   Private Const HP_HASHVAL As Long = &H2
   
   Private Declare Function CryptGetHashParam Lib "advapi32.dll" (ByVal hHash As Long, ByVal dwParam As Long, pByte As Any, ByRef pdwDataLen As Long, ByVal dwFlags As Long) As Long
   Private Declare Function CryptAcquireContext Lib "advapi32.dll" Alias "CryptAcquireContextA" (ByRef phProv As Long, ByVal pszContainer As String, ByVal pszProvider As String, ByVal dwProvType As Long, ByVal dwFlags As Long) As Long
   Private Declare Function CryptCreateHash Lib "advapi32.dll" (ByVal hProv As Long, ByRef Algid As Long, ByRef hKey As Long, ByVal dwFlags As Long, ByRef phHash As Long) As Long
   Private Declare Function CryptDestroyHash Lib "advapi32.dll" (ByRef hHash As Long) As Long
   Private Declare Function CryptHashData Lib "advapi32.dll" (ByRef hHash As Long, ByVal pbData As String, ByVal dwDataLen As Long, ByVal dwFlags As Long) As Long
   Private Declare Function CryptReleaseContext Lib "advapi32.dll" (ByRef hProv As Long, ByRef dwFlags As Long) As Long
   
   Public Function MD5DigestToHex(ByVal s As String) As String
       Dim hContext As Long
       Dim hHash As Long
       Dim dwDataLen As Long
       Dim b() As Byte
       
       '-- Acquire our crypto context and create a hash object
       CryptAcquireContext hContext, vbNullChar, vbNullChar, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT
       CryptCreateHash hContext, ByVal CALG_MD5, ByVal 0&, 0&, hHash
       
       '-- Add our data to the hash
       CryptHashData ByVal hHash, s, Len(s), 0
       
       '-- Get the length of the data in the hash (should always be 16 bytes - 32 hex chars)
       CryptGetHashParam hHash, HP_HASHSIZE, dwDataLen, 4, 0
       '-- Set up data buffer for the hashed data
       ReDim b(dwDataLen - 1)
       '-- Get the hashed data
       CryptGetHashParam hHash, HP_HASHVAL, b(0), dwDataLen, 0
       
       Dim str As String, i As Long
       '-- Convert to hex
       For i = 0 To UBound(b)
           str = str & HexChar(b(i))
       Next i
       MD5DigestToHex = LCase$(str)
       
       '-- Clean up
       CryptDestroyHash hHash
       CryptReleaseContext hContext, ByVal 0&
   End Function
   
   Private Function HexChar(ByVal b As Byte) As String
       Dim s As String
       s = Hex(b)
       If Len(s) = 1 Then s = "0" & s
       HexChar = s
   End Function

The code first acquires a context in which to do our CryptoAPI work, and then creates a hash object so that we can hash some data. We then hash the input string using CryptHashData, and finally get the size of the hashed data (which should always be 16 bytes, for this example because we're using MD5) and then the actual data. The data is in binary format so we then have to convert it to hex (well we don't really, but it's usually more convenient like that). The only thing left to do then is return the value and clean up. You can also change the algorithm that it uses to hash the data by using a different "CALG_xxxx" constant when creating the hash with CryptCreateHash. A list of the supported algorithms can be found here.