String Hashing in VBA

Visual Basic for Applications · Wikibooks

0%

Contents

    Text

    Size
    100%
    Theme
    Typeface
    Line length
    Spacing

    From Wikibooks, under CC BY-SA 4.0, as the source states it. This page at Wikibooks.

    String Hashing in VBA

    Summary

    • The VBA code below generates the digests for the MD5, SHA1, SHA2-256, SHA2-384, and SHA2-512 hashes; in this case for strings.
    • A hash is an output string that resembles a pseudo random sequence, and is essentially unique for any string that is used as its starting value. Hashes cannot be easily cracked to find the string that was used in their making and they are very sensitive to input change. That is to say, just a change in one character at the start will produce a completely different output. Hashes can be used as the basis of pseudo random character tables, and although not purely random, such methods can produce output quality that is at least as good as the in-built Rnd() function of VBA.
    • The use of a hash allows programmers to avoid the embedding of password strings in their code.
      • The memory space occupied by an application can be read with special utilities, so passwords might be found in code, then used in a normal user login. Instead, the hash of the password is listed in code, and the password is hashed for comparison only during a logon. This avoids access to the application via the conventional user route, since any hash that is found could not be reverse engineered to obtain the value needed at the user interface. This method assumes that the code cannot be run by the intruder at any location other than the logon device, and that they are unable to change the memory contents.
      • If a hacker can change the memory contents, then a common exploit is to change the hash in memory for one of their own; one that corresponds to a password that they can use at the user logon interface. The counter action against this attack is for all of the logon files to be encrypted with the user's officially issued password. Then, even if the hash is changed, the files needed for the logon attempt cannot be decrypted for use.
    • Hashes can also be made from entire files, and the code for doing so differs only slightly from the string hashing versions given below. The main difference in file hashing is that the file is first turned into a string before using conventional techniques. Code is given elsewhere in this series for file hashing. String hashes will produce an output even when the empty string is used as a starting point, unlike for file hashing where an empty text file can raise errors.
    • This VBA code is not specific for any one application, so it will work in any of say, MS Word, MS Excel, or MS Access. These code versions include options for base-64 output or hex.

    Code Listings

    Notes on the Code

    IMPORTANT. It was found that the hash routines errored in a Windows 10, 64 bit Office setup. However, subsequent checking revealed the solution. The Windows platform must have installed the Net Framework 3.5 (includes .Net 2 and .Net 3), this older version, and not only the Net Framework 4.8 Advanced Services that was enabled in Turn Windows Features on and off. When it was selected there, the routines worked perfectly.

    The VBA code below generates the digests for the MD5, SHA1, SHA2-256, SHA2-384, and SHA2-512 hashes, for strings, in either of the hex or base-64 output formats. These codings each make use of MS Office's built-in functions, and provide consistent results. It has been noted that original implementations elsewhere for the same digests can differ widely in their outputs. Only one example has been given with a seed or salt parameter (StrToSHA512Salt), and it should be noted that the HMACSHA512 class output differs from the SHA*Managed class hashes given in the remainder. The Managed classes give the best widely reported results. Note the VBA references required for correct operation. A reminder of these is given in some procedure headings.

    In each case, coders can find the unmodified hash values in the bytes() array and at that point they are in 8-bit bytes, that is, the numbers that represent the ASCI code as it applies to a full eight-bit, 256 character set. The code that follows the filling of the bytes() array in each case decides which version of the ASCI character set to deliver. For a hex set of characters, 0-9, and A to F, the total bit set is broken into double the number of four-bit bytes, then returned for use. For the base-64 set, lower case letters, upper case letters, and integers mainly, six bit characters are made for output. These two sets are the most useful here, since they consist of commonly used characters. The 128 and 256 ASCI sets are too full of both exotic and non-printing characters to be useful. For each hash version its bit count is a constant, so the length of its output will vary according to the chosen type.

    If your data is in ANSI, you will get different results between Excel/ACCESS and SQL Server when using T-SQL HASHBYTES() function for characters code over 128. To solve those differences use StrConv() instead of .GetBytes_4()

    'dont use for ANSI
    Set oT = CreateObject("System.Text.UTF8Encoding")
    TextToHash = oT.GetBytes_4(sIn)
    'for ANSI data use StrConv instead
    TextToHash = StrConv(sIn, vbFromUnicode)
    


    Option Explicit
    
    Sub Test()
        'run this to test md5, sha1, sha2/256, sha384, sha2/512 with salt, or sha2/512
        'Check that Net Framework 3.5 (includes .Net 2 and .Net 3 is installed in windows
        'and not just Net Advanced Services 
        
        Dim sIn As String, sOut As String, b64 As Boolean
        Dim sH As String, sSecret As String
        
        'insert the text to hash within the sIn quotes
        'and for selected procedures a string for the secret key
        sIn = ""
        sSecret = "" 'secret key for StrToSHA512Salt only
        
        'select as required
        'b64 = False   'output hex
        b64 = True   'output base-64
        
        'enable any one
        'sH = MD5(sIn, b64)
        'sH = SHA1(sIn, b64)
        'sH = SHA256(sIn, b64)
        'sH = SHA384(sIn, b64)
        'sH = StrToSHA512Salt(sIn, sSecret, b64)
        sH = SHA512(sIn, b64)
        
        'message box and immediate window outputs
        Debug.Print sH & vbNewLine & Len(sH) & " characters in length"
        MsgBox sH & vbNewLine & Len(sH) & " characters in length"
        
        'de-comment this block to place the hash in first cell of sheet1
    '    With ThisWorkbook.Worksheets("Sheet1").Cells(1, 1)
    '        .Font.Name = "Consolas"
    '        .Select: Selection.NumberFormat = "@" 'make cell text
    '        .Value = sH
    '    End With
    
    End Sub
    
    Public Function MD5(ByVal sIn As String, Optional bB64 As Boolean = 0) As String
        'Set a reference to mscorlib 4.0 64-bit
        'Check that Net Framework 3.5 (includes .Net 2 and .Net 3 is installed in windows
        'and not just Net Advanced Services 
            
        'Test with empty string input:
        'Hex:   d41d8cd98f00...etc
        'Base-64: 1B2M2Y8Asg...etc
            
        Dim oT As Object, oMD5 As Object
        Dim TextToHash() As Byte
        Dim bytes() As Byte
            
        Set oT = CreateObject("System.Text.UTF8Encoding")
        Set oMD5 = CreateObject("System.Security.Cryptography.MD5CryptoServiceProvider")
     
        TextToHash = oT.GetBytes_4(sIn)
        bytes = oMD5.ComputeHash_2((TextToHash))
     
        If bB64 = True Then
           MD5 = ConvToBase64String(bytes)
        Else
           MD5 = ConvToHexString(bytes)
        End If
            
        Set oT = Nothing
        Set oMD5 = Nothing
    
    End Function
    
    Public Function SHA1(sIn As String, Optional bB64 As Boolean = 0) As String
        'Set a reference to mscorlib 4.0 64-bit
        'Check that Net Framework 3.5 (includes .Net 2 and .Net 3 is installed in windows
        'and not just Net Advanced Services 
       
       'Test with empty string input:
        '40 Hex:   da39a3ee5e6...etc
        '28 Base-64:   2jmj7l5rSw0yVb...etc
        
        Dim oT As Object, oSHA1 As Object
        Dim TextToHash() As Byte
        Dim bytes() As Byte
                
        Set oT = CreateObject("System.Text.UTF8Encoding")
        Set oSHA1 = CreateObject("System.Security.Cryptography.SHA1Managed")
        
        TextToHash = oT.GetBytes_4(sIn)
        bytes = oSHA1.ComputeHash_2((TextToHash))
            
        If bB64 = True Then
           SHA1 = ConvToBase64String(bytes)
        Else
           SHA1 = ConvToHexString(bytes)
        End If
                
        Set oT = Nothing
        Set oSHA1 = Nothing
        
    End Function
    
    Public Function SHA256(sIn As String, Optional bB64 As Boolean = 0) As String
        'Set a reference to mscorlib 4.0 64-bit
        'Check that Net Framework 3.5 (includes .Net 2 and .Net 3 is installed in windows
        'and not just Net Advanced Services 
        
        'Test with empty string input:
        '64 Hex:   e3b0c44298f...etc
        '44 Base-64:   47DEQpj8HBSa+/...etc
        
        Dim oT As Object, oSHA256 As Object
        Dim TextToHash() As Byte, bytes() As Byte
        
        Set oT = CreateObject("System.Text.UTF8Encoding")
        Set oSHA256 = CreateObject("System.Security.Cryptography.SHA256Managed")
        
        TextToHash = oT.GetBytes_4(sIn)
        bytes = oSHA256.ComputeHash_2((TextToHash))
        
        If bB64 = True Then
           SHA256 = ConvToBase64String(bytes)
        Else
           SHA256 = ConvToHexString(bytes)
        End If
        
        Set oT = Nothing
        Set oSHA256 = Nothing
        
    End Function
    
    Public Function SHA384(sIn As String, Optional bB64 As Boolean = 0) As String
        'Set a reference to mscorlib 4.0 64-bit
        'Check that Net Framework 3.5 (includes .Net 2 and .Net 3 is installed in windows
        'and not just Net Advanced Services 
        
        'Test with empty string input:
        '96 Hex:   38b060a751ac...etc
        '64 Base-64:   OLBgp1GsljhM2T...etc
        
        Dim oT As Object, oSHA384 As Object
        Dim TextToHash() As Byte, bytes() As Byte
        
        Set oT = CreateObject("System.Text.UTF8Encoding")
        Set oSHA384 = CreateObject("System.Security.Cryptography.SHA384Managed")
        
        TextToHash = oT.GetBytes_4(sIn)
        bytes = oSHA384.ComputeHash_2((TextToHash))
        
        If bB64 = True Then
           SHA384 = ConvToBase64String(bytes)
        Else
           SHA384 = ConvToHexString(bytes)
        End If
        
        Set oT = Nothing
        Set oSHA384 = Nothing
        
    End Function
    
    Public Function SHA512(sIn As String, Optional bB64 As Boolean = 0) As String
        'Set a reference to mscorlib 4.0 64-bit
        'Check that Net Framework 3.5 (includes .Net 2 and .Net 3 is installed in windows
        'and not just Net Advanced Services 
        
        'Test with empty string input:
        '128 Hex:   cf83e1357eefb8bd...etc
        '88 Base-64:   z4PhNX7vuL3xVChQ...etc
        
        Dim oT As Object, oSHA512 As Object
        Dim TextToHash() As Byte, bytes() As Byte
        
        Set oT = CreateObject("System.Text.UTF8Encoding")
        Set oSHA512 = CreateObject("System.Security.Cryptography.SHA512Managed")
        
        TextToHash = oT.GetBytes_4(sIn)
        bytes = oSHA512.ComputeHash_2((TextToHash))
        
        If bB64 = True Then
           SHA512 = ConvToBase64String(bytes)
        Else
           SHA512 = ConvToHexString(bytes)
        End If
        
        Set oT = Nothing
        Set oSHA512 = Nothing
        
    End Function
    
    Function StrToSHA512Salt(ByVal sIn As String, ByVal sSecretKey As String, _
                               Optional ByVal b64 As Boolean = False) As String
        'Returns a sha512 STRING HASH in function name, modified by the parameter sSecretKey.
        'This hash differs from that of SHA512 using the SHA512Managed class.
        'HMAC class inputs are hashed twice;first input and key are mixed before hashing,
        'then the key is mixed with the result and hashed again.
        'Check that Net Framework 3.5 (includes .Net 2 and .Net 3 is installed in windows
        'and not just Net Advanced Services 
        
        Dim asc As Object, enc As Object
        Dim TextToHash() As Byte
        Dim SecretKey() As Byte
        Dim bytes() As Byte
        
        'Test results with both strings empty:
        '128 Hex:    b936cee86c9f...etc
        '88 Base-64:   uTbO6Gyfh6pd...etc
        
        'create text and crypto objects
        Set asc = CreateObject("System.Text.UTF8Encoding")
        
        'Any of HMACSHAMD5,HMACSHA1,HMACSHA256,HMACSHA384,or HMACSHA512 can be used
        'for corresponding hashes, albeit not matching those of Managed classes.
        Set enc = CreateObject("System.Security.Cryptography.HMACSHA512")
    
        'make a byte array of the text to hash
        bytes = asc.Getbytes_4(sIn)
        'make a byte array of the private key
        SecretKey = asc.Getbytes_4(sSecretKey)
        'add the private key property to the encryption object
        enc.Key = SecretKey
    
        'make a byte array of the hash
        bytes = enc.ComputeHash_2((bytes))
        
        'convert the byte array to string
        If b64 = True Then
           StrToSHA512Salt = ConvToBase64String(bytes)
        Else
           StrToSHA512Salt = ConvToHexString(bytes)
        End If
        
        'release object variables
        Set asc = Nothing
        Set enc = Nothing
    
    End Function
    
    Private Function ConvToBase64String(vIn As Variant) As Variant
        'Check that Net Framework 3.5 (includes .Net 2 and .Net 3 is installed in windows
        'and not just Net Advanced Services 
       
       Dim oD As Object
          
        Set oD = CreateObject("MSXML2.DOMDocument")
          With oD
            .LoadXML "<root />"
            .DocumentElement.DataType = "bin.base64"
            .DocumentElement.nodeTypedValue = vIn
          End With
        ConvToBase64String = Replace(oD.DocumentElement.Text, vbLf, "")
        
        Set oD = Nothing
    
    End Function
    
    Private Function ConvToHexString(vIn As Variant) As Variant
        'Check that Net Framework 3.5 (includes .Net 2 and .Net 3 is installed in windows
        'and not just Net Advanced Services 
        
        Dim oD As Object
          
        Set oD = CreateObject("MSXML2.DOMDocument")
          
          With oD
            .LoadXML "<root />"
            .DocumentElement.DataType = "bin.Hex"
            .DocumentElement.nodeTypedValue = vIn
          End With
        ConvToHexString = Replace(oD.DocumentElement.Text, vbLf, "")
        
        Set oD = Nothing
    
    End Function
    

    See Also

    • File Hashing in VBA  : A companion page in this series that lists code for single file hashing. Combine this with file listing code for extensive hashing of files.
    • Folder Hashing in VBA :Another companion page that makes recursive folder hash listings, and logs. Uses up to date hash algorithms, but limited to files no larger than about 200MB.