How to get the MD5 hex hash for a file using VBA?

34,851

An older question that could use a better answer. These functions are specifically for hashing files, not for hashing passwords. As a bonus, I'm including a function for SHA1. If you get rid of the type declarations these functions work in VBScript too except that the GetFileBytes function needs to be changed to use FileSystemObject (or possibly ADO Stream) as the Free File doesn't exist in VBScript.

Private Sub TestMD5()
    Debug.Print FileToMD5Hex("C:\test.txt")
    Debug.Print FileToSHA1Hex("C:\test.txt")
End Sub

Public Function FileToMD5Hex(sFileName As String) As String
    Dim enc
    Dim bytes
    Dim outstr As String
    Dim pos As Integer
    Set enc = CreateObject("System.Security.Cryptography.MD5CryptoServiceProvider")
    'Convert the string to a byte array and hash it
    bytes = GetFileBytes(sFileName)
    bytes = enc.ComputeHash_2((bytes))
    'Convert the byte array to a hex string
    For pos = 1 To LenB(bytes)
        outstr = outstr & LCase(Right("0" & Hex(AscB(MidB(bytes, pos, 1))), 2))
    Next
    FileToMD5Hex = outstr
    Set enc = Nothing
End Function

Public Function FileToSHA1Hex(sFileName As String) As String
    Dim enc
    Dim bytes
    Dim outstr As String
    Dim pos As Integer
    Set enc = CreateObject("System.Security.Cryptography.SHA1CryptoServiceProvider")
    'Convert the string to a byte array and hash it
    bytes = GetFileBytes(sFileName)
    bytes = enc.ComputeHash_2((bytes))
    'Convert the byte array to a hex string
    For pos = 1 To LenB(bytes)
        outstr = outstr & LCase(Right("0" & Hex(AscB(MidB(bytes, pos, 1))), 2))
    Next
    FileToSHA1Hex = outstr 'Returns a 40 byte/character hex string
    Set enc = Nothing
End Function

Private Function GetFileBytes(ByVal path As String) As Byte()
    Dim lngFileNum As Long
    Dim bytRtnVal() As Byte
    lngFileNum = FreeFile
    If LenB(Dir(path)) Then ''// Does file exist?
        Open path For Binary Access Read As lngFileNum
        ReDim bytRtnVal(LOF(lngFileNum) - 1&) As Byte
        Get lngFileNum, , bytRtnVal
        Close lngFileNum
    Else
        Err.Raise 53
    End If
    GetFileBytes = bytRtnVal
    Erase bytRtnVal
End Function
Share:
34,851
aF.
Author by

aF.

Last time I checked,                 I was me.

Updated on July 26, 2022

Comments

  • aF.
    aF. almost 2 years

    How can I get the MD5 hex hash for a file using VBA?

    I need a version that works for a file.

    Something as simple as this Python code:

    import hashlib
    
    def md5_for_file(fileLocation, block_size=2**20):
        f = open(fileLocation)
        md5 = hashlib.md5()
        while True:
            data = f.read(block_size)
            if not data:
                break
            md5.update(data)
        f.close()
        return md5.hexdigest()
    

    But in VBA.

  • Ben
    Ben almost 12 years
    Huh? First, that's VB.NET not VBA and second, you're omitting some very important Import commands.
  • Michael Zlatkovsky - Microsoft
    Michael Zlatkovsky - Microsoft almost 12 years
    Oops, I had mis-read VBA as VB.NET. VBA would be a fair bit harder, since it does not have all of the .NET framework support that makes the above code so simple. As for imports, Visual Studio will probably suggest those for you automatically, but for completion's sake, they are System.IO and System.Security.Cryptography.
  • B Hart
    B Hart over 10 years
    Quick question: The variable "asc" for the UTF8Encoding isn't used anywhere, does this serve a purpose? Also, for it to work with VBScript you will have to probably open the file using an ADODB.Stream object instead of the FreeFile method... In any case Great share!
  • HK1
    HK1 over 10 years
    I think the "asc" stuff must have been artifacts from when I was using this code to hash passwords. I've removed it now. And yes, Free File doesn't exist in VBScript. I did find a function that I think could be made to work that uses the File System Object: stackoverflow.com/questions/6060529/…
  • Nigel Heffernan
    Nigel Heffernan over 6 years
    Good solution, with a couple of nits to pick... Dim bytes() As Byte offers a small gain; and passing it by reference into a reconfigured Private Sub GetFileBytes(sFileName As String, arrBytes() As Byte) means that you sidestep a redundant memory allocation - and that's a real gain, for resource usage and performance. The elephant in the room is that, for really large files, ReDim bytRtnVal(LOF(lngFileNum) - 1&) As Byte will raise errors. But I can't post anything better, because I do not know of any 'chunking' or streaming API into the System.Security.Cryptography functions.
  • Nigel Heffernan
    Nigel Heffernan over 6 years
    Update: user Florent B. posted an answer with data passed in chunks to the MD5 hashing service in this StackOverflow answer