Last active
June 3, 2022 07:42
-
-
Save advanceboy/9a4200eb8e06cb9fda5326bf41e338cb to your computer and use it in GitHub Desktop.
As an alternative to Get-FileHash, this script computes the hash value with multiple algorithms at the same time.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Requires -Version 5.0 | |
[CmdletBinding(DefaultParameterSetName = "Path")] | |
param( | |
[Parameter(Mandatory, ParameterSetName="Path", Position = 0)] | |
[System.String[]] | |
$Path, | |
[Parameter(Mandatory, ParameterSetName="LiteralPath", ValueFromPipelineByPropertyName = $true)] | |
[Alias("PSPath")] | |
[System.String[]] | |
$LiteralPath, | |
[Parameter(Mandatory, ParameterSetName="Stream")] | |
[System.IO.Stream] | |
$InputStream, | |
[ValidateSet("SHA1", "SHA256", "SHA384", "SHA512", "MD5")] | |
[ValidateNotNullOrEmpty()] | |
[System.String[]] | |
$Algorithms=@("SHA256"), | |
[ValidateRange(1, 2GB)] | |
[int] | |
$BufferSize=8MB | |
); | |
begin { | |
Set-StrictMode -Version Latest; | |
trap { break; } | |
$algorithmTypeInfos = @(); | |
$algorithmTypeInfos += $Algorithms | ForEach-Object { | |
$algorithmType = "System.Security.Cryptography.$_" -as [Type]; | |
if (!$algorithmType) { | |
throw "Not Supported: $_"; | |
} | |
Write-Output @{Name = $_; Type = $algorithmType }; | |
}; | |
$hashCoreType = Add-Type -Language CSharp -PassThru -TypeDefinition @" | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Security.Cryptography; | |
using System.Threading.Tasks; | |
public static class HashCore { | |
public static void Hash(Stream inputStream, IEnumerable<HashAlgorithm> hashers, int bufferSize) { | |
var bin1 = new byte[bufferSize]; | |
var bin2 = new byte[bufferSize]; | |
byte[] bint; | |
int read = 0; | |
var hasherArray = hashers.ToArray(); | |
var streams = new CryptoStream[hasherArray.Length]; | |
var waitTasks = new Task[hasherArray.Length + 1]; | |
try { | |
for (var i = 0; i < hasherArray.Length; i++) { | |
streams[i] = new CryptoStream(Stream.Null, hasherArray[i], CryptoStreamMode.Write); | |
} | |
do { | |
var readTask = inputStream.ReadAsync(bin1, 0, bufferSize); | |
for (var i = 0; i < hasherArray.Length; i++) { | |
waitTasks[i] = streams[i].WriteAsync(bin2, 0, read); | |
} | |
waitTasks[hasherArray.Length] = readTask; | |
Task.WaitAll(waitTasks); | |
read = readTask.Result; | |
bint = bin1; | |
bin1 = bin2; | |
bin2 = bint; | |
} while (read > 0); | |
for (var i = 0; i < hasherArray.Length; i++) { | |
streams[i].FlushFinalBlock(); | |
} | |
} finally { | |
foreach (var stream in streams) { | |
if (stream != null) { | |
stream.Dispose(); | |
} | |
} | |
} | |
} | |
} | |
"@; | |
function Get-StreamHash ([System.IO.Stream]$InputStream) { | |
$hashers = New-Object System.Security.Cryptography.HashAlgorithm[]($algorithmTypeInfos.Length); | |
try { | |
for ($i = 0; $i -lt $algorithmTypeInfos.Length; $i++) { | |
$hashers[$i] = $algorithmTypeInfos[$i].Type::Create(); | |
} | |
$hashCoreType::Hash($InputStream, $hashers, $BufferSize); | |
for ($i = 0; $i -lt $algorithmTypeInfos.Length; $i++) { | |
[ordered]@{Algorithm = $algorithmTypeInfos[$i].Name; Hash = [System.BitConverter]::ToString([byte[]]$hashers[$i].Hash) -replace '-',''}; | |
} | |
} finally { | |
foreach ($hasher in $hashers) { | |
if ($null -ne $hasher) { | |
$hasher.Dispose(); | |
} | |
} | |
} | |
} | |
} | |
process { | |
$results = @(); | |
if ($PSCmdlet.ParameterSetName -eq 'Stream') { | |
$results += Get-StreamHash -InputStream $InputStream; | |
} else { | |
$filePaths = @(); | |
if ($PSCmdlet.ParameterSetName -eq 'Path') { | |
$filePaths += Resolve-Path $Path | ForEach-Object -MemberName ProviderPath; | |
} | |
if ($PSCmdlet.ParameterSetName -eq 'LiteralPath') { | |
$filePaths += Resolve-Path -LiteralPath $LiteralPath | ForEach-Object -MemberName ProviderPath; | |
} | |
foreach ($filepath in $filePaths) { | |
$stream = [System.IO.File]::OpenRead($filePath); | |
try { | |
$currentResults = Get-StreamHash -InputStream $stream; | |
foreach ($result in $currentResults) { | |
$result['Path'] = $filePath; | |
} | |
$results += $currentResults; | |
} finally { | |
$stream.Dispose(); | |
} | |
} | |
} | |
foreach ($result in $results) { | |
$retVal = [PSCustomObject]$result; | |
$retVal.PSObject.TypeNames.Insert(0, "Microsoft.Powershell.Utility.FileHash"); | |
$retVal; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment