PS: Verify Hashes

This script can verify checksum files. In the default mode it scans a given path for hash files and verifies them. But also combined files with a lot of hashes can be verified.


Requirements

  • PowerShell

Usage

To run the script just call it and provide the required parameters

Parameters:

  • InputPath
    [STRING] Mandatory parameter that provides the source path for the archive operations
  • ResultsOutput
    [STRING] screen (default) means output is written to screen.
    otherwise use a filename for the results.
  • HashFileNameAllFiles
    [STRING] Filename of the hash file that contains a list (no individual) with files and their hashes
    When specified the check gets performed otherwise inly individual files get verified.
  • LogFilePath
    [STRING] Path where the log file should be created (If logging is enabled)
  • LogFileBaseName
    [STRING] Name of the log file. This string gets appended to the date time value (
  • LogFileDateTime)
    Default path is the script executable directory
  • LogFileDateTime
    [STRING] Date time formatting string that can be used to create log files on for example a daily basis (yyyyMMdd)
  • CheckHashForEachFile
    [BOOL] Enabled by default it creates hash file for each file that gets processed.
  • OnlyShowErrors
    [BOOL] Enabled by default it only shows files with errors not the once that are Ok.
  • NoLogging
    [BOOL] Enabled by default. This parameter can be used to prevent log file creation
  • DebugLogging
    [BOOL] Disabled by default. This parameter can be used to enable debug logging (this increases the size of the log file)
    To enable debug logging the parameter NoLogging needs to be $ false
  • DebugToScreen
    [BOOL] Disabled by default. When enabled and DebugLogging is also enabled all information written to the log file get displayed
    also to the console window.

Script code:

	
##------------------------------------------------------------------------------------------------
##
##  Verify-Hashes.ps1
##
##   Version 1.0.0
##
##   (c) 2016 Martin Mueller
##		www.sh-soft.com
##
##  Licence: Feel free to use and redistribute this script!
##
##------------------------------------------------------------------------------------------------

<#
.SYNOPSIS
Reads given files and checks the files against previously created checksums.


.DESCRIPTION
Reads given files and checks the files against previously created checksums.
 

.PARAMETER InputPath

[STRING] Mandatory parameter that provides the source path for the archive operations


.PARAMETER ResultsOutput

[STRING] screen (default) means output is written to screen.

otherwise use a filename for the results.


.PARAMETER HashFileNameAllFiles

[STRING] Filename of the hash file that contains a list (no individual) with files and their hashes 

When specified the check gets performed otherwise inly individual files get verified.


.PARAMETER LogFilePath

[STRING] Path where the log file should be created (If logging is enabled)


.PARAMETER LogFileBaseName

[STRING] Name of the log file. This string gets appended to the date time value (.PARAMETER LogFileDateTime)

Default path is the script executable directory


.PARAMETER LogFileDateTime

[STRING] Date time formatting string that can be used to create log files on for example a daily basis (yyyyMMdd)


.PARAMETER CheckHashForEachFile

[BOOL] Enabled by default it creates hash file for each file that gets processed.


.PARAMETER OnlyShowErrors

[BOOL] Enabled by default it only shows files with errors not the once that are Ok.


.PARAMETER NoLogging

[BOOL] Enabled by default. This parameter can be used to prevent log file creation


.PARAMETER DebugLogging

[BOOL] Disabled by default. This parameter can be used to enable debug logging (this increases the size of the log file)

To enable debug logging the parameter NoLogging needs to be $false


.PARAMETER DebugToScreen

[BOOL] Disabled by default. When enabled and DebugLogging is also enabled all information written to the log file get displayed 

also to the console window.



.EXAMPLE
Quick hash verification with default parameters
.\verify-Hashes.ps1 -InputPath "C:\inetpub\logs\LogFiles"
#>

#------------------------------------------------------------------------------------------------
# Parameter block
#------------------------------------------------------------------------------------------------
param( 
	[Parameter(Mandatory=$TRUE)] 
		[String] $InputPath, 
	[String] $ResultsOutput = "screen",
	[String] $HashFileNameAllFiles="",
	[String] $LogFilePath = (Split-Path -Parent -Path $MyInvocation.MyCommand.Definition),
	[String] $LogFileBaseName = "_CheckHash-Job.log",
	[String] $LogFileDateTime = "yyyyMMdd.HHmmss",
	[bool] $CheckHashForEachFile = $true,
	[bool] $OnlyShowErrors = $true,
	[bool] $NoLogging = $true, 
	[bool] $DebugLogging = $false,
	[bool] $DebugToScreen = $false 
)

##------------------------------------------------------------------------------------------------
## Initialization
##------------------------------------------------------------------------------------------------
begin {
	#------------------------------------------------------------------------------------------------
	# Configure Environment and basic functions
	#------------------------------------------------------------------------------------------------

	# LOGWRITER
	$Date = Get-Date -Format $LogFileDateTime
	function Write-Log ($Entry, $DebugEntry) {
		if (-not $NoLogging) {
			$DateTime = Get-Date -Format "yyyyMMdd;HHmmss;"
			$FileName = Join-Path -Path $LogFilePath -ChildPath ($Date + $LogFileBaseName)
		
			if ($DebugToScreen) {
				if ($Entry) {
					Write-Host -ForegroundColor Cyan ($DateTime+$Entry)
				}
				if ($DebugEntry) {
					Write-Host -ForegroundColor Magenta ($DateTime+"[DEBUG]") -nonewline
					Write-Host -ForegroundColor Cyan $DebugEntry
				}
			}
			if ($LogFilePath.Length -eq 0) {
				$FileName = ($Date + "-" + $LogFileBaseName)
			}
			if ($Entry) {
				Add-Content -Path $FileName -Value ($DateTime + $Entry)
			}
			if ($DebugLogging -and $DebugEntry) {
				Add-Content -Path $FileName -Value ($DateTime +" [DEBUG] "+ $DebugEntry)
			}
		}
	}
	# FILEHASH GENERATOR
	function Create-FileHash ($FileName, $HashAlgorithm) {
		switch ($HashAlgorithm) { 
			"md5" { 
				$ExpectedHashSize = 128
				$HashfileEnding = "md5"
				$CProvider = new-object System.Security.Cryptography.MD5CryptoServiceProvider
				Write-Log -DebugEntry ("Settings for algorithm: $HashfileEnding applied!")
				break
			} 
			"sha1" { 
				$ExpectedHashSize = 160
				$HashfileEnding = "sha"
				$CProvider = new-object System.Security.Cryptography.SHA1CryptoServiceProvider
				Write-Log -DebugEntry ("Settings for algorithm: $HashfileEnding applied!")
				break
			} 
			"sha256" { 
				$ExpectedHashSize = 256
				$HashfileEnding = "sha256"
				try { 
					$CProvider = new-object System.Security.Cryptography.SHA256CryptoServiceProvider 
					Write-Log -DebugEntry ("Settings for algorithm: $HashfileEnding applied!")
				}
				catch { 
					Write-Log -DebugEntry ("Settings for algorithm: $HashfileEnding failed!")
					Write-Error -Message "SHA256 seems not to work on this system..." 
				}			
				break
			} 
			"sha512" { 
				$ExpectedHashSize = 512
				$HashfileEnding = "sha512"
				try {
					$CProvider = new-object System.Security.Cryptography.SHA512CryptoServiceProvider 
					Write-Log -DebugEntry ("Settings for algorithm: $HashfileEnding applied!")
				}
				catch { 
					Write-Log -DebugEntry ("Settings for algorithm: $HashfileEnding failed!")
					Write-Error -Message "SHA256 seems not to work on this system..." 
				}
				break
			} 
			default { 
				$HashfileEnding = "sha"
				$CProvider = new-object System.Security.Cryptography.SHA1CryptoServiceProvider
				Write-Log -DebugEntry ("Settings for algorithm: $HashfileEnding applied!")
			}
		} 
		# Fail back ‎algorithm
		if ($CProvider.HashSize -ne $ExpectedHashSize) {
			$CProvider = new-object System.Security.Cryptography.SHA1CryptoServiceProvider
			Write-Warning -Message "Failing back to SHA1 hash algorithm..."
			Write-Log -DebugEntry ("Failing back to SHA1 hash algorithm")
		}
		try {
			$file = [System.IO.File]::Open($FileName,[System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
			$HASH = (([System.BitConverter]::ToString($CProvider.ComputeHash($file))).Replace("-", "")).ToLower()
			$file.Dispose()
			return ($HASH)
		}
		catch {
			Write-Log -Entry "[ERROR] creating hash for file: $FileName"
			return ("[ERROR]")
		}
	}
	
	### USER INTERFACE
	Write-Log -Entry "Beginning hash verification..." -DebugEntry (Get-Date)
	Write-Host -ForegroundColor Green "Beginning hash verification..."
	Write-Host -ForegroundColor Yellow "LogFilePath: " -NoNewline
	Write-Host $LogFilePath
	Write-Host -ForegroundColor Yellow "Input path: " -NoNewline
	Write-Host $InputPath
	# CHECK: InputPath clean up
	if ($InputPath[($InputPath.Length)-1] -ne "\") {
		$InputPath = $InputPath+"\"
	}
	$InputPath = $InputPath.ToLower()

	#------------------------------------------------------------------------------------------------
	# CHECK: Input path...
	if (-not (Test-Path -Path $InputPath)) {
		Write-Log -Entry "[Error] Input Path not accessible"
		Write-Error "[ERROR] Input Path not accessible"
		exit
	}

	#------------------------------------------------------------------------------------------------
	# CHECK: Hashing ‎algorithm
	
	### USER INTERFACE
	Write-Host -ForegroundColor Green "Hash algorithm used: " -NoNewline
	Write-Host "Autodetect"
	
	#------------------------------------------------------------------------------------------------
	# function to verify hash entry from file
	function Verify-HashEntry ([string]$Entry, $HashFileName) {
		$HashFromFile = $Entry.Split(' ')[0]
		#Determine hashing algorithm used based on hash length
		$HashAlgorithm = ""
		switch ($HashFromFile.Length) {
			32 { $HashAlgorithm = "md5" }
			40 { $HashAlgorithm = "sha1" }
			64 { $HashAlgorithm = "sha256" }
			128 { $HashAlgorithm = "sha512" }
		}
		Write-Log -DebugEntry "Hash from file: $HashFromFile ($HashAlgorithm)"

		$FileNameFromFile = $Entry.Replace($HashFromFile+' ', "")
		$FileNameFromFile = $FileNameFromFile.Replace('*', "")
		
		if ($HashFileName) { #When paths comes from individual file the parent path comes from the hash file itself
			$InputPath = Split-Path $HashFileName -Parent
		}
		$FileNameFromFile = Join-Path -Path "$InputPath" -ChildPath "$FileNameFromFile"

		$Result = "ERROR"
		if (Test-Path -Path $FileNameFromFile) {
			# create hash
			$FileHash = Create-FileHash -Filename $FileNameFromFile -HashAlgorithm $HashAlgorithm
			if ($HashFromFile -eq $FileHash) {
				$Result = "Ok"
			}
			else {
				$Result = ("ERROR - Hash mismatch!")
			}
		}
		else {
			$Result = "ERROR - File not found or inaccessible!"
		}
		return $Result, $HashFromFile, $FileNameFromFile, $FileHash
	}
	
} 


##------------------------------------------------------------------------------------------------
## Main block
##------------------------------------------------------------------------------------------------
process { 
	#------------------------------------------------------------------------------------------------
	# Retrieve files in InputDirectory
	#------------------------------------------------------------------------------------------------
	$HashFileList = @()
	$HashFileList = Get-ChildItem -Path "$InputPath" -Filter ("*.sha*") -Recurse | Select-Object FullName, FileNameFromFile, HashFromFile, FileHash, Result
	$HashFileList += Get-ChildItem -Path "$InputPath" -Filter ("*.md5") -Recurse | Select-Object FullName, FileNameFromFile, HashFromFile, FileHash, Result
		
	##------------------------------------------------------------------------------------------------
	## verify individual hashes
	##------------------------------------------------------------------------------------------------
	if ($CheckHashForEachFile) {
		$Progress = 0
		$TotalCount = $HashFileList.Count
		$HashFileList | foreach {
			$HashFileName = $_.FullName
			$Result = "ERROR"
			try {
				#Contents from hash file
				$CurrentContent = Get-Content -Path "$HashFileName" -Encoding UTF8
				#Process only files with one line content!
				if ($CurrentContent.Count -gt 1) {
					$CurrentContent = $CurrentContent[0]
				}

				$CurrentContentResult = Verify-HashEntry -Entry "$CurrentContent" -HashFileName $HashFileName
				$Result = $CurrentContentResult[0]
				$_.HashFromFile = $CurrentContentResult[1]
				$_.FileNameFromFile = $CurrentContentResult[2]
				$_.FileHash = $CurrentContentResult[3]
			}
			catch {
				$Result = ("ERROR - reading hash file: "+$HashFileName)
			}
			$_.Result = $Result
			#Progress bar
			Write-Progress -Activity "Checking individual hash files" -CurrentOperation ("Processing hash file: $Progress of $TotalCount ("+$_.FileNameFromFile+")") -PercentComplete ($Progress / $TotalCount * 100) -Status "working"
			$Progress++
		}
		Write-Progress -Activity "Checking individual hash files" -Completed
	}


	##------------------------------------------------------------------------------------------------
	## verify overall hash file
	##------------------------------------------------------------------------------------------------
	$OverallHashFileList = @()
	# | Select-Object -Property FileNameFromFile, HashFromFile, FileHash, Result
	if ($HashFileNameAllFiles.Length -gt 0) {
		$Progress = 0
		
		try {
			$HashList = Get-Content -Path $HashFileNameAllFiles -Encoding UTF8
			$TotalCount = $Hashlist.Count
			$Hashlist| ForEach-Object {
				$CurrentContent = $_
				$CurrentContentResult = Verify-HashEntry -Entry "$CurrentContent"
				$CurrentCheck = New-Object System.Object
				$CurrentCheck | Add-Member -Name Result -MemberType NoteProperty -Value $CurrentContentResult[0]
				$CurrentCheck | Add-Member -Name HashFromFile -MemberType NoteProperty -Value $CurrentContentResult[1]
				$CurrentCheck | Add-Member -Name FileNameFromFile -MemberType NoteProperty -Value $CurrentContentResult[2]
				$CurrentCheck | Add-Member -Name FileHash -MemberType NoteProperty -Value $CurrentContentResult[3]
				$OverallHashFileList += $CurrentCheck
				Write-Progress -Activity "Checking combined hash file: $HashFileNameAllFiles" -CurrentOperation ("Processing file: $Progress of $TotalCount ("+$CurrentContentResult[2]+")") -PercentComplete ($Progress / $TotalCount * 100) -Status "working"
				$Progress++
			}
		} 
		catch {
			Write-Log -Entry "[ERROR] reading overall hash file" -DebugEntry $HashFileNameAllFiles
		}
	}
	
	
	##------------------------------------------------------------------------------------------------
	## output Results
	##------------------------------------------------------------------------------------------------
	if ($ResultsOutput -eq "screen") {
		if ($CheckHashForEachFile) {
			Write-Host -ForegroundColor Yellow "Individual files checked:"
			Write-Host -ForegroundColor Yellow " Files total: " -NoNewline
				Write-Host ($HashFileList.Count)
			Write-Host -ForegroundColor Yellow " Files Ok:	" -NoNewline
				Write-Host (($HashFileList | Where-Object {$_.Result -eq "Ok"} | Measure-Object | Select-Object -ExpandProperty Count))
			Write-Host -ForegroundColor Yellow " Files ERROR: " -NoNewline
				Write-Host (($HashFileList | Where-Object {$_.Result -ne "Ok"} | Measure-Object | Select-Object -ExpandProperty Count))
			Write-Host -ForegroundColor Yellow "File list:"
			Write-Host -ForegroundColor Yellow "------------------------"
			if ($OnlyShowErrors) {
				$HashFileList | Where-Object {$_.Result -ne "Ok"} | Format-Table -Property FileNameFromFile, Result -AutoSize
			}
			else {
				$HashFileList | Format-Table -Property FileNameFromFile, Result -AutoSize
			}
		}
		if ($HashFileNameAllFiles.Length -gt 0) {
			Write-Host -ForegroundColor Yellow "Files checked from combined file:"
			Write-Host -ForegroundColor Yellow " Files total: " -NoNewline
				Write-Host ($OverallHashFileList.Count)
			Write-Host -ForegroundColor Yellow " Files Ok:	" -NoNewline
				Write-Host (($OverallHashFileList | Where-Object {$_.Result -eq "Ok"} | Measure-Object | Select-Object -ExpandProperty Count))
			Write-Host -ForegroundColor Yellow " Files ERROR: " -NoNewline
				Write-Host (($OverallHashFileList | Where-Object {$_.Result -ne "Ok"} | Measure-Object | Select-Object -ExpandProperty Count))
			Write-Host -ForegroundColor Yellow "File list:"
			Write-Host -ForegroundColor Yellow "------------------------"
			if ($OnlyShowErrors) {
				$OverallHashFileList | Where-Object {$_.Result -ne "Ok"} | Format-Table -Property FileNameFromFile, Result -AutoSize
			}
			else {
				$OverallHashFileList | Format-Table -Property FileNameFromFile, Result -AutoSize
			}
		}
	}
	else {
		try {
			Set-Content -Path $ResultsOutput -Encoding UTF8 -Value ("Verify-Hashes-Result")
			Add-Content -Path $ResultsOutput -Encoding UTF8 -Value ("Check time: "+(Get-Date -Format "yyyyMMdd;HHmmss;"))
			Add-Content -Path $ResultsOutput -Encoding UTF8 -Value ("Check individual has files enabled: $CheckHashForEachFile")
			if ($HashFileNameAllFiles.Length -gt 0) {
				Add-Content -Path $ResultsOutput -Encoding UTF8 -Value ("Check combined hash file: $HashFileNameAllFiles")
			}
			if ($CheckHashForEachFile) {
				Add-Content -Path $ResultsOutput -Encoding UTF8 -Value ("Individual files checked:"+$HashFileList.Count)
				Add-Content -Path $ResultsOutput -Encoding UTF8 -Value (" files Ok: "+($HashFileList | Where-Object {$_.Result -eq "Ok"} | Measure-Object | Select-Object -ExpandProperty Count))
				Add-Content -Path $ResultsOutput -Encoding UTF8 -Value (" files Error: "+($HashFileList | Where-Object {$_.Result -ne "Ok"} | Measure-Object | Select-Object -ExpandProperty Count))
				Add-Content -Path $ResultsOutput -Encoding UTF8 -Value ("File list:")
				if ($OnlyShowErrors) {
					$HashFileList | Where-Object {$_.Result -ne "Ok"} | ForEach-Object {
						Add-Content -Path $ResultsOutput -Encoding UTF8 -Value ($_.FileNameFromFile+";"+$_.Result)
					}
				}
				else {
					$HashFileList | ForEach-Object {
						Add-Content -Path $ResultsOutput -Encoding UTF8 -Value ($_.FileNameFromFile+";"+$_.Result)
					}
				}
			}
			if ($HashFileNameAllFiles.Length -gt 0) {
				Add-Content -Path $ResultsOutput -Encoding UTF8 -Value ("Files checked from combined file:"+$OverallHashFileList.Count)
				Add-Content -Path $ResultsOutput -Encoding UTF8 -Value (" files Ok: "+($OverallHashFileList | Where-Object {$_.Result -eq "Ok"} | Measure-Object | Select-Object -ExpandProperty Count))
				Add-Content -Path $ResultsOutput -Encoding UTF8 -Value (" files Error: "+($OverallHashFileList | Where-Object {$_.Result -ne "Ok"} | Measure-Object | Select-Object -ExpandProperty Count))
				Add-Content -Path $ResultsOutput -Encoding UTF8 -Value ("File list:")
				if ($OnlyShowErrors) {
					$OverallHashFileList | Where-Object {$_.Result -ne "Ok"} | ForEach-Object {
						Add-Content -Path $ResultsOutput -Encoding UTF8 -Value ($_.FileNameFromFile+";"+$_.Result)
					}
				}
				else {
					$OverallHashFileList | ForEach-Object {
						Add-Content -Path $ResultsOutput -Encoding UTF8 -Value ($_.FileNameFromFile+";"+$_.Result)
					}
				}
			}
			Write-Host -ForegroundColor Yellow "Results have been written to file: "-NoNewline
			Write-Host $ResultsOutput
		}
		catch {
			Write-Error "Failed to create result file!"
		}
	}
		
	#------------------------------------------------------------------------------------------------		
	# Finished
	Write-Log -Entry "Operation finished! Script will end now." -DebugEntry (Get-Date)
} 

	

Download the script:

>> Version 1.0.0 (current)
(MD5: d663006cf849470ac85e5133fc397a45)
(SHA1: 43a8471cf147403c5dace2e16d2c454c332cf099)


>> syntax highlighting powered by highlight.js