应用程序 VBA/VBA 中的文件哈希
- 本节包含用于生成文件哈希的代码,即对整个文件的哈希。
- 提供了多种算法,并提供用于 base64 或十六进制的输出选项。下面的 VBA 代码生成 MD5、SHA1、SHA2-256、SHA2-384 和 SHA2-512 哈希的摘要。
- 此代码适用于单个文件,但在相邻页面上提供的代码,VBA 中的文件夹哈希,可用于递归哈希列表,同样可以选择哈希和输出选项。
- 字符串哈希例程在另一节中给出。
- 通常这些哈希不使用种子值,但为了说明该方法,代码包含一个这样的示例(FileToSHA512SALT())。请注意,它的输出与SHA512Managed类的输出不同。如果对其他加盐(带种子的)输入感兴趣,则在相应的过程中存在注释。
- 这些列出的算法可以对长度不超过 200MB(兆字节)的任何单个文件进行哈希,超过此长度,GetFileBytes() 中将生成内存不足错误。具体测试发现,哈希对于 200MB 的压缩文件效果很好,但对于 500MB 的压缩文件则失败;确切的断点尚不清楚。对于大于 200MB 的文件,存在其他工具。
- 大文件哈希,例如超过 200MB,最好使用其他工具完成。这里提到了四个这样的例子
- Microsoft 的 FCIV 实用工具,是免费下载。它是一个命令行应用程序,能够对单个文件和整个文件夹树进行哈希。它可以轻松处理大型文件,但只适用于 MD5 和 SHA1 哈希。它将 base64 和十六进制输出都发送到屏幕,但只将 b64 输出格式发送到文件。准备好的文件可以与任何新运行进行验证,但结果只能显示在屏幕上。它有点难以使用,即使有他们的说明,所以页面从 VBA 运行 FCIV 实用工具和文件校验和完整性验证程序 (FCIV) 示例可能会被新手发现有用。到目前为止,微软还没有将编码扩展到包括现代算法。
- Windows 8.1 及更高版本中的 PowerShell,可以使用所有 MD5、SHA1、SHA256、SHA384 和 SHA512 算法创建大型单个文件哈希。它只在屏幕上生成输出,尽管输出也可以被管道传输到剪贴板以供粘贴。没有用于对文件夹进行哈希或将输出输出到 xml 文件的简单选项。为了完整起见,文件校验和完整性验证程序 (FCIV) 示例中给出了一个使用示例。在 Windows 10 中,也可以使用certutil -hashfile <要哈希的文件的完整路径> MD5,在命令提示符下获取哈希值,尽管大小限制尚不清楚。(将 md5 更改为 sha1、sha256 或 sha512 等)。
- 可以处理大型文件的外部应用程序是MD5 和 SHA 校验和实用程序。它是一个独立的应用程序,基本版本可以免费下载。它为单个文件生成 MD5、SHA1、SHA2/256 和 SHA2/512 哈希。输出为十六进制,并在简洁的用户界面上一起显示。还有一个更复杂的商业版本。
- FSUM 快速文件完整性检查器是另一个用于命令行使用的免费外部应用程序。它在许多方面类似于 FCIV,但包含最新的算法(MD2、MD4、MD5、SHA-1、SHA-2(256、384、512)、RIPEMD-160、PANAMA、TIGER、ADLER32 和 CRC32)。除了大型文件十六进制哈希外,它还可以执行平面或递归文件夹哈希。输入的代码与 FCIV 的代码不完全相同,但提供了一个文本文件,其中包含使用示例。网页FSUM 快速文件完整性检查器提供了下载和其他详细信息,虽然文本文件没有提到结果可以轻松地通过管道传输到剪贴板,方法是 |clip。虽然图形界面存在于其他地方,但命令行应用程序被发现是最稳定的。
- 在尝试哈希时,需要考虑文件的权限。哈希必须访问文件才能获取它们包含的字节。虽然这并不涉及实际运行文件,但某些文件夹和文件类型可能会在运行时被发现被锁定。事实上,这种类型的访问是字符串哈希和文件哈希之间的主要区别。每当访问文件时,通常需要错误处理。这里假设用户会添加自己的错误处理,或者会在哈希尝试之前绕过有问题的文件。用户应该知道,代码无法处理空文本文件;例如,没有保存任何文本的记事本文件。GetFileBytes 例程将出错。如果遇到空文件,或者遇到超过 200MB 的文件,将产生一条消息并退出。
- 用户文件和文件夹几乎没有限制。除了空文件问题外,那些想要访问自己创建的文件夹中的用户文件的人通常不会遇到任何问题,并且感兴趣的人应该知道,在本系列的另一节中有一个递归文件夹哈希模块可能与之相关。 VBA 中的文件夹哈希还包含有关如何避免音乐、视频和其他 Microsoft 库的虚拟文件夹问题的说明。
- 哈希只关心文件的内容,而不是它的名称或其他文件详细信息。这意味着可以找到任何名称下文件的副本,方法是比较它们的哈希值。在具有故意混淆的文件名的安全系统中,这意味着可以对非常长的文件列表进行哈希,直到找到所需的哈希值,而不是依赖于不太安全的文件名来查找它。或者,文件名有时只是文件的哈希值,以便哈希可以揭示任何错误或非法更改。在这种情况下,黑客可能会更改文件,然后使用相应的哈希对文件进行命名,但他不知道所需的哈希算法或要使用的私有字符串,因此当所有者运行他自己的哈希验证时,更改将始终被检测到。
重要。发现哈希例程在 Windows 10、64 位 Office 设置中出错。但是,随后的检查揭示了解决方案。Windows 平台必须已安装Net Framework 3.5(包括 .Net 2 和 .Net 3),这个旧版本,而不仅仅是打开或关闭 Windows 功能中启用的Net Framework 4.8 高级服务。当它在那里被选中时,例程工作得很好。
- 2020 年 9 月 11 日添加了将结果传输到剪贴板的默认代码
- 2019 年 7 月 25 日将文件选择对话框设置为打开时列出所有文件类型
- 2019 年 6 月 17 日添加了文件选择对话框和文件大小限制
在该集合中的其他地方提供了用于对字符串进行哈希和批量文件哈希的代码。下面的面板包含与字符串相同的代码,但经过了轻微的修改,用于对单个完整文件进行哈希。用户通过选择对话框提供文件的完整路径作为起始参数。参数选项允许选择十六进制或 base-64 输出。包括用于 MD5、SHA1、SHA2-256、SHA2-384 和 SHA2-512 哈希的函数。
对于频繁使用,选择对话框最方便,虽然代码包含一个已注释的行,供那些打算将文件地址键入过程的人使用;只需注释掉不需要的行即可。
在每种情况下,编码人员可以在 bytes() 数组中找到未修改的哈希值,此时它们处于 8 位字节,即表示 ASCII 代码的数字,它适用于完整的 8 位 256 字符集。在每种情况下,填充 bytes() 数组后的代码决定要交付的 ASCII 字符集版本。对于十六进制字符集,0-9 和 A 到 F,总位集被分成四位字节数量的两倍,然后返回以供使用。对于 base-64 集,主要使用小写字母、大写字母和整数,为输出创建 6 位字符。这两个集合在这里最有用,因为它们包含常用的字符。128 和 256 ASCII 集包含太多奇特字符和非打印字符,因此没有用。对于每个哈希版本,其位计数都是一个常数,因此其输出长度将根据所选类型而变化。
一般而言,消息框不允许复制其文本。如果需要复制,请将消息框替换为输入框,并将输出哈希设置为框的默认值。然后可以轻松地将其复制。或者,使用立即窗口中 *Debug.Print* 方法的输出。已包含一个过程来用结果覆盖剪贴板:如果不需要此功能,请在顶层过程中注释掉该行。
Option Explicit
Public sFPath As String, sH As String
Private Sub TestFileHashes()
'run this to obtain file hashes in a choice of algorithms
'select any one algorithm call below
'Limited to unrestricted files less than 200MB and not zero
'Set a reference to mscorlib 4.0 64-bit, and Microsoft Scripting Runtime
'Make sure that Net Framework 3.5 (includes .Net 2 and .Net 3) is installed and enabled
'and not only the Net Framework 4.8 Advanced Services
Dim b64 As Boolean, bOK As Boolean, bOK2 as Boolean
Dim sSecret As String, nSize As Long, reply
'USER SETTINGS
'======================================================
'======================================================
'set output format here
b64 = True 'true for output base-64, false for hex
'======================================================
'set chosen file here
'either set path to target file in hard-typed line
'or choose a file using the file dialog procedure
'sFPath = "C:\Users\Your Folder\Documents\test.txt" 'eg.
sFPath = SelectFile2("SELECT A FILE TO HASH...") 'uses file dialog
'check the file
If sFPath = "" Then 'exit sub for no file selection
MsgBox "No selection made - closing"
Exit Sub
End If
bOK = GetFileSize(sFPath, nSize)
If nSize = 0 Or nSize > 200000000 Then 'exit sub for zero size
MsgBox "File has zero contents or greater than 200MB - closing"
Exit Sub
End If
'======================================================
'set secret key here if using HMAC class of algorithms
sSecret = "Set secret key for FileToSHA512Salt selection"
'======================================================
'choose algorithm
'enable any one line to obtain that hash result
'sH = FileToMD5(sFPath, b64)
'sH = FileToSHA1(sFPath, b64)
'sH = FileToSHA256(sFPath, b64)
'sH = FileToSHA384(sFPath, b64)
'sH = FileToSHA512Salt(sFPath, sSecret, b64)
sH = FileToSHA512(sFPath, b64)
'======================================================
'======================================================
'Results Output - open the immediate window as required
Debug.Print sFPath & vbNewLine & sH & vbNewLine & Len(sH) & " characters in length"
MsgBox sFPath & vbNewLine & sH & vbNewLine & Len(sH) & " characters in length"
'reply = InputBox("The selected text can be copied with Ctrl-C", "Output is in the box...", sH)
'decomment these two lines to overwrite the clipboard with the results
bOK2 = CopyToClip(sH)
If bOK2 = True Then MsgBox ("The result is on the clipboard.")
'decomment this line to append the hash to a file (after setting its path)
'AppendHashToFile
'decomment 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
Private Sub AppendHashToFile()
Dim sFPath2 As String, fso As FileSystemObject, ts As TextStream
Dim sContents As String, sNewContents As String
sFPath2 = "C:\Users\Your Folder\Documents\test.txt" 'eg.
Set fso = New FileSystemObject
If Not Dir(sFPath2) = vbNullString Then
'docs.microsoft.com/office/vba/language/reference/user-interface-help/opentextfile-method
'devblogs.microsoft.com/scripting/how-can-i-add-a-line-to-the-top-of-a-text-file/
Set ts = fso.OpenTextFile(sFPath2, ForReading)
sContents = ts.ReadAll: ts.Close
End If
sNewContents = sH & vbTab & sFPath & vbTab & Now & vbNewLine & sContents
sNewContents = Left(sNewContents, Len(sNewContents) - 2)
Set ts = fso.OpenTextFile(sFPath2, ForWriting, True)
ts.WriteLine sNewContents: ts.Close
End Sub
Public Function FileToMD5(sFullPath As String, Optional bB64 As Boolean = False) As String
'parameter full path with name of file returned in the function as an MD5 hash
'Set a reference to mscorlib 4.0 64-bit
'Make sure that Net Framework 3.5 (includes .Net 2 and .Net 3) is installed and enabled
'and not only the Net Framework 4.8 Advanced Services
Dim enc, bytes, outstr As String, pos As Integer
Set enc = CreateObject("System.Security.Cryptography.MD5CryptoServiceProvider")
'Convert the string to a byte array and hash it
bytes = GetFileBytes(sFullPath)
bytes = enc.ComputeHash_2((bytes))
If bB64 = True Then
FileToMD5 = ConvToBase64String(bytes)
Else
FileToMD5 = ConvToHexString(bytes)
End If
Set enc = Nothing
End Function
Public Function FileToSHA1(sFullPath As String, Optional bB64 As Boolean = False) As String
'parameter full path with name of file returned in the function as an SHA1 hash
'Set a reference to mscorlib 4.0 64-bit
'Make sure that Net Framework 3.5 (includes .Net 2 and .Net 3) is installed and enabled
'and not only the Net Framework 4.8 Advanced Services
Dim enc, bytes, outstr As String, pos As Integer
Set enc = CreateObject("System.Security.Cryptography.SHA1CryptoServiceProvider")
'Convert the string to a byte array and hash it
bytes = GetFileBytes(sFullPath) 'returned as a byte array
bytes = enc.ComputeHash_2((bytes))
If bB64 = True Then
FileToSHA1 = ConvToBase64String(bytes)
Else
FileToSHA1 = ConvToHexString(bytes)
End If
Set enc = Nothing
End Function
Function FileToSHA512Salt(ByVal sPath As String, ByVal sSecretKey As String, _
Optional ByVal bB64 As Boolean = False) As String
'Returns a sha512 FILE HASH in function name, modified by parameter sSecretKey.
'This hash differs from that of FileToSHA512 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.
'Set a reference to mscorlib 4.0 64-bit
'Make sure that Net Framework 3.5 (includes .Net 2 and .Net 3) is installed and enabled
'and not only the Net Framework 4.8 Advanced Services
Dim asc As Object, enc As Object
Dim SecretKey() As Byte
Dim bytes() As Byte
'create a 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 = GetFileBytes(sPath)
'make a byte array of the private key
SecretKey = asc.Getbytes_4(sSecretKey)
'add the key property
enc.Key = SecretKey
'make a byte array of the hash
bytes = enc.ComputeHash_2((bytes))
'convert the byte array to string
If bB64 = True Then
FileToSHA512Salt = ConvToBase64String(bytes)
Else
FileToSHA512Salt = ConvToHexString(bytes)
End If
'release object variables
Set asc = Nothing
Set enc = Nothing
End Function
Public Function FileToSHA256(sFullPath As String, Optional bB64 As Boolean = False) As String
'parameter full path with name of file returned in the function as an SHA2-256 hash
'Set a reference to mscorlib 4.0 64-bit
'Make sure that Net Framework 3.5 (includes .Net 2 and .Net 3) is installed and enabled
'and not only the Net Framework 4.8 Advanced Services
Dim enc, bytes, outstr As String, pos As Integer
Set enc = CreateObject("System.Security.Cryptography.SHA256Managed")
'Convert the string to a byte array and hash it
bytes = GetFileBytes(sFullPath) 'returned as a byte array
bytes = enc.ComputeHash_2((bytes))
If bB64 = True Then
FileToSHA256 = ConvToBase64String(bytes)
Else
FileToSHA256 = ConvToHexString(bytes)
End If
Set enc = Nothing
End Function
Public Function FileToSHA384(sFullPath As String, Optional bB64 As Boolean = False) As String
'parameter full path with name of file returned in the function as an SHA2-384 hash
'Set a reference to mscorlib 4.0 64-bit
'Make sure that Net Framework 3.5 (includes .Net 2 and .Net 3) is installed and enabled
'and not only the Net Framework 4.8 Advanced Services
Dim enc, bytes, outstr As String, pos As Integer
Set enc = CreateObject("System.Security.Cryptography.SHA384Managed")
'Convert the string to a byte array and hash it
bytes = GetFileBytes(sFullPath) 'returned as a byte array
bytes = enc.ComputeHash_2((bytes))
If bB64 = True Then
FileToSHA384 = ConvToBase64String(bytes)
Else
FileToSHA384 = ConvToHexString(bytes)
End If
Set enc = Nothing
End Function
Public Function FileToSHA512(sFullPath As String, Optional bB64 As Boolean = False) As String
'parameter full path with name of file returned in the function as an SHA2-512 hash
'Set a reference to mscorlib 4.0 64-bit
'Make sure that Net Framework 3.5 (includes .Net 2 and .Net 3) is installed and enabled
'and not only the Net Framework 4.8 Advanced Services
Dim enc, bytes, outstr As String, pos As Integer
Set enc = CreateObject("System.Security.Cryptography.SHA512Managed")
'Convert the string to a byte array and hash it
bytes = GetFileBytes(sFullPath) 'returned as a byte array
bytes = enc.ComputeHash_2((bytes))
If bB64 = True Then
FileToSHA512 = ConvToBase64String(bytes)
Else
FileToSHA512 = ConvToHexString(bytes)
End If
Set enc = Nothing
End Function
Private Function GetFileBytes(ByVal sPath As String) As Byte()
'makes byte array from file
'Set a reference to mscorlib 4.0 64-bit
'Make sure that Net Framework 3.5 (includes .Net 2 and .Net 3) is installed and enabled
'and not only the Net Framework 4.8 Advanced Services
Dim lngFileNum As Long, bytRtnVal() As Byte, bTest
lngFileNum = FreeFile
If LenB(Dir(sPath)) Then ''// Does file exist?
Open sPath For Binary Access Read As lngFileNum
'a zero length file content will give error 9 here
ReDim bytRtnVal(0 To LOF(lngFileNum) - 1&) As Byte
Get lngFileNum, , bytRtnVal
Close lngFileNum
Else
Err.Raise 53 'File not found
End If
GetFileBytes = bytRtnVal
Erase bytRtnVal
End Function
Function ConvToBase64String(vIn As Variant) As Variant
'used to produce a base-64 output
'Set a reference to mscorlib 4.0 64-bit
'Make sure that Net Framework 3.5 (includes .Net 2 and .Net 3) is installed and enabled
'and not only the Net Framework 4.8 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
Function ConvToHexString(vIn As Variant) As Variant
'used to produce a hex output
'Set a reference to mscorlib 4.0 64-bit
'Make sure that Net Framework 3.5 (includes .Net 2 and .Net 3) is installed and enabled
'and not only the Net Framework 4.8 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
Function GetFileSize(sFilePath As String, nSize As Long) As Boolean
'use this to test for a zero file size
'takes full path as string in sFilePath
'returns file size in bytes in nSize
'Make a reference to Scripting Runtime
'Make sure that Net Framework 3.5 (includes .Net 2 and .Net 3) is installed and enabled
'and not only the Net Framework 4.8 Advanced Services
Dim fs As FileSystemObject, f As File
Set fs = CreateObject("Scripting.FileSystemObject")
If fs.FileExists(sFilePath) Then
Set f = fs.GetFile(sFilePath)
nSize = f.Size
GetFileSize = True
Exit Function
End If
End Function
Function SelectFile2(Optional sTitle As String = "") As String
'opens a file-select dialog and on selection
'returns its full path string in the function name
'If Cancel or OK without selection, returns empty string
'Make sure that Net Framework 3.5 (includes .Net 2 and .Net 3) is installed and enabled
'and not only the Net Framework 4.8 Advanced Services
Dim fd As FileDialog, sPathOnOpen As String, sOut As String
Set fd = Application.FileDialog(msoFileDialogFilePicker)
'do not include backslash here
sPathOnOpen = Application.DefaultFilePath
'set the file-types list on the dialog and other properties
With fd
.Filters.Clear
'the first filter line below sets the default on open (here all files are listed)
.Filters.Add "All Files", "*.*"
.Filters.Add "Excel workbooks", "*.xlsx;*.xlsm;*.xls;*.xltx;*.xltm;*.xlt;*.xml;*.ods"
.Filters.Add "Word documents", "*.docx;*.docm;*.dotx;*.dotm;*.doc;*.dot;*.odt"
.AllowMultiSelect = False
.InitialFileName = sPathOnOpen
.Title = sTitle
.InitialView = msoFileDialogViewList 'msoFileDialogViewSmallIcons
.Show
If .SelectedItems.Count = 0 Then
'MsgBox "Canceled without selection"
Exit Function
Else
sOut = .SelectedItems(1)
'MsgBox sOut
End If
End With
SelectFile2 = sOut
End Function
Function CopyToClip(sIn As String) As Boolean
'passes the parameter string to the clipboard
'set reference to Microsoft Forms 2.0 Object Library (by browsing for FM20.DLL).
'If ref not listed, inserting user form will list it.
'Clipboard cleared when launch application closes.
'Make sure that Net Framework 3.5 (includes .Net 2 and .Net 3) is installed and enabled
'and not only the Net Framework 4.8 Advanced Services
Dim DataOut As DataObject
Set DataOut = New DataObject
'first pass textbox text to dataobject
DataOut.SetText sIn
'then pass dataobject text to clipboard
DataOut.PutInClipboard
'release object variable
Set DataOut = Nothing
CopyToClip = True
End Function
- VBA 中的字符串哈希 : 此系列中的一个配套页面,适用于只想对字符串进行哈希处理的用户。
- VBA 中的文件夹哈希 : 另一个配套页面,它创建递归文件夹哈希列表和日志。使用最新的哈希算法,但仅限于大小不超过约 200 MB 的文件。
- 从 VBA 运行 FCIV 实用程序: 如何使用 Microsoft fciv.exe 命令行实用程序从 VBA 创建 MD5 和 SHA1 文件哈希。虽然使用起来比较笨拙,但 MS 实用程序允许对整个文件夹树进行哈希处理和验证。
- 文件校验和完整性验证器 (FCIV) 示例: 有关如何使用 FCIV 实用程序的更多详细信息,适用于没有太多使用命令行提示符经验的用户。
- MD5 和 SHA 校验和实用程序: 一个外部网站的免费应用程序,用于同时显示单个文件的 MD5、SHA1、SHA256 和 SHA512 哈希值(仅十六进制)。包括文件浏览功能和拖放功能,使操作变得轻松。此实用程序还可以用于对大型文件进行哈希处理;作者已测试过 500 MB 文件。
- FSUM 快速文件完整性检查器 : FSUM 哈希器的基本命令行版本下载。
- FSUM GUI : FSUM 实用程序的图形界面下载网站。这允许文件浏览、拖放以及其他功能,以简化原本的命令行操作。