Use PowerShell To Calculate the Hash of a File

I’m pretty sure that everyone has something similar to a hash calculator that has been written in PowerShell or some other language and I am no different. This is my take on a simple file hash calculator to determine the hash of a given file or files based off of a given algorithm.

For those of you who may not know what I am talking about, calculating a file hash is useful to determine if a file has been modified from its current state. Usually when you download a file from the internet, there will be an associated checksum that has the expected hash of the file you are downloading. If the hashes match up, then there is nothing to worry about. But if the hash of the downloaded file differs from the expected hash, then that may raise concern as to the integrity of the file and if it has been compromised in any way.

To calculate a hash for a file, I will use the Security.Cryptography.HashAlgorithm class and its static method Create() which will create an instance of a given algorithm to use for the calculation. So what are my possible algorithm choices? Good question, they are:

  • MD5 
  • SHA1
  • SHA256
  • SHA384
  • SHA512
  • RIPEM160

For this example, I will just stick with MD5 and continue by creating the instance.

$algorithm = [Security.Cryptography.HashAlgorithm]::Create("MD5")

From this instance, I will then use the ComputeHash() method which will allow me to compute the hash of a given file based on whether I supply a file stream or the bytes of a file. First I will use the bytes method to show how easy it is to get the MD5 hash from a file.

$fileBytes = [io.File]::ReadAllBytes("C:\users\Administrator\desktop\NewFile.txt")
$bytes = $algorithm.ComputeHash($fileBytes)
-Join ($bytes | ForEach {"{0:x2}" -f $_})

image

First, I get the read all of the bytes of the file into memory and then use the ComputeHash() method of the MD5 instance and then translate that into a hex output using the {0:x2} formatter and make it all on one line by specifying –Join at the beginning line of code. While this method does work great, the gotcha is that this will start to fall apart when working with larger files. If you try this on a 3GB file  for instance and you don’t have the memory to support it, then you will the operation fail due to insufficient memory.

image

The Factory.wim file is 4Gb in size and is more than double what the ReadAllBytes() method allows.

The way to get around this issue is by using the other possible parameter that ComputeHash accepts which is a stream.

$hash = [Security.Cryptography.HashAlgorithm]::Create( "MD5" )
$stream = ([IO.StreamReader]"C:\users\Administrator\desktop\newfile.txt").BaseStream
-join ($hash.ComputeHash($stream) | ForEach { "{0:x2}" -f $_ })
$stream.Close()

image

Same amount of code and the same results. But the difference here is that if I ran this against a large file, it wouldn’t freak out with an error about memory running out. Lets try that factory.wim file again and see what happens.

image

It took a little bit of time, but I had no issues with getting the hash of the 4Gb file using the stream. So with that, your best option is to use a stream to handle any file, regardless of size. This will save you from a hassle if you happen to try to calculate a hash for a larger file.

Of course, this is always better as a function that can be re-used with little to no effort and wouldn’t be nicer to allow the option to pick multiple algorithms or be able to run Get-ChildItem and then pipe the output into a function to get the hashes? Of course it would be! That is why I have a function called Get-FileHash that will do all of this for you. It is a simple function that performs a simple hash calculation of all of the possible hashes if you choose.

Lets take a look at some examples of using it.

Get-FileHash -Path .\NewFile.txt -Algorithm MD5

image

Get-FileHash -Path .\newfile.txt -Algorithm MD5,SHA1,SHA512,SHA256,RIPEMD160 | Format-List

image

Get-ChildItem -Filter *.txt | 
    Get-FileHash -Algorithm MD5

image

This even works against UNC paths as well!

image

And yes, it works against files of any size, such as the 4GB image.

:\dell\Image\Factory.wim -Algorithm SHA256

The download for this function is available at the link below. As always, thank you for stopping by and I am always interested in hearing what you think about this function.

 

Download Get-FileHash

Script Repository

This entry was posted in powershell, scripts and tagged , , , , , . Bookmark the permalink.

14 Responses to Use PowerShell To Calculate the Hash of a File

  1. Get-FileHash -Path .\newfile.txt -Algorithm MD5,SHA1,SHA512,SHA256,RIPEMD160 | Format-List doesn’t seem to work for me.

    Someone told me that the -Algorithm variable doesn’t accept multiple kinds in a single command. I thought the cmdlet would be fine to execute, but I tried several times trying to run this command and it just returns me errors.

    Although the single algorithm variable works, as you can see here:
    http://vvcap.com/2PxUkZWs3Uc

  2. toffe says:

    h i
    how can I get files in power shell by using hash values ?

  3. Sanjay says:

    Thanks for the nice article! How do I calculate or get the hash of a remote file, downloaded via HTTP?

  4. Nassim says:

    Great function Boe, just one comment :

    When using the function together with “Get-ChildItem -Recurse”, the hash value is calculated only for the files in the top level directory.

    The command I’ve used :

    Get-ChildItem -Recurse | Select FullName, Length, @{N=’MD5′;E={(Get-FileHash -Algorithm MD5 $_).MD5 }}

  5. Pingback: Calculate Sha-1 Hash.js | Fix Runtime Errors & BSOD's

  6. Kristoffer Rudolph says:

    Nice function. One further comment:
    Using multiple algorithms, the result of the 2nd and further algorithms is always the hash of emptystring, because ComputeHash() reads from streams current position.The stream position has to be reseted after every alogorithm.

  7. Boe, This has become a regular part of my toolkit, thank you very much!

  8. Lee Holmes says:

    Very nice. Two minor comments:
    1) This is a useful way to convert a path to its full path: $filename = (Resolve-Path $file).Path
    2) You forgot to close the StreamReader. This can temporarily leave the file locked until .NET’s garbage collection gets around to cleaning it up.

    Lee

    • Boe Prox says:

      Thanks Lee! I really appreciate the comments and feedback, especially on using Resolve-Path. Such a rookie mistake on my part by not closing the stream :). I’ve update the script and article to reflect both of these suggestions.

Leave a comment