Chciałbym obliczyć sumę kontrolną MD5 jakiejś treści. Jak to zrobić w PowerShell?
Chciałbym obliczyć sumę kontrolną MD5 jakiejś treści. Jak to zrobić w PowerShell?
Odpowiedzi:
Jeśli treść jest ciągiem:
$someString = "Hello, World!"
$md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
$utf8 = New-Object -TypeName System.Text.UTF8Encoding
$hash = [System.BitConverter]::ToString($md5.ComputeHash($utf8.GetBytes($someString)))
Jeśli zawartość jest plikiem:
$someFilePath = "C:\foo.txt"
$md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
$hash = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes($someFilePath)))
Począwszy od wersji PowerShell 4, można to łatwo zrobić dla plików po wyjęciu z pudełka za pomocą polecenia Get-FileHash
cmdlet:
Get-FileHash <filepath> -Algorithm MD5
Jest to z pewnością lepsze, ponieważ pozwala uniknąć problemów, które oferuje pierwsze rozwiązanie, jak określono w komentarzach (używa strumienia, zamyka go i obsługuje duże pliki).
Exception calling "ReadAllBytes" with "1" argument(s): "The file is too long. This operation is currently limited to supporting files less than 2 gigabytes in size."
Jako użytkownik Linuksa nowy w Powershell, jestem bardzo zirytowany problemami, jakie mam z uzyskaniem sumy md5, która byłaby po prostu md5sum file.ext
na Linuksie.
$hash = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::Open("$someFilePath",[System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)))
zapewnia to niskie zużycie pamięci i brak limitu 2 GB .
$stream = [System.IO.File]::Open("$someFilePath",[System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
Następnie $hash = [System.BitConverter]::ToString($md5.ComputeHash($stream))
następnie$stream.Close()
Jeśli używasz rozszerzeń społeczności PowerShell, istnieje polecenie Get-Hash, które zrobi to łatwo:
C:\PS> "hello world" | Get-Hash -Algorithm MD5
Algorithm: MD5
Path :
HashString : E42B054623B3799CB71F0883900F2764
Get-FileHash
w waniliowym PowerShell 4.0. Vide TechNet .
Oto dwie linie, po prostu zmień „cześć” w linii nr 2:
PS C:\> [Reflection.Assembly]::LoadWithPartialName("System.Web")
PS C:\> [System.Web.Security.FormsAuthentication]::HashPasswordForStoringInConfigFile("hello", "MD5")
Oto funkcja, której używam, która obsługuje ścieżki względne i bezwzględne:
function md5hash($path)
{
$fullPath = Resolve-Path $path
$md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
$file = [System.IO.File]::Open($fullPath,[System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
try {
[System.BitConverter]::ToString($md5.ComputeHash($file))
} finally {
$file.Dispose()
}
}
Podziękowania dla @davor powyżej za sugestię użycia Open () zamiast ReadAllBytes () oraz dla @ jpmc26 za sugestię użycia końcowego bloku.
Dispose
Połączenie powinno być w finally
bloku.
Innym wbudowanym poleceniem, które od dawna jest domyślnie instalowane w systemie Windows od 2003 r., Jest Certutil , który oczywiście można również wywołać z PowerShell.
CertUtil -hashfile file.foo MD5
(Uwaga: MD5 powinien być we wszystkich zakrętkach, aby zapewnić maksymalną wytrzymałość)
FipsAlgorithmPolicy
jest włączona.
Istnieje wiele przykładów online wykorzystujących ComputeHash (). Moje testy wykazały, że działało to bardzo wolno, gdy działało przez połączenie sieciowe. Poniższy fragment kodu działa znacznie szybciej, ale Twój przebieg może się różnić:
$md5 = [System.Security.Cryptography.MD5]::Create("MD5")
$fd = [System.IO.File]::OpenRead($file)
$buf = New-Object byte[] (1024*1024*8) # 8 MB buffer
while (($read_len = $fd.Read($buf,0,$buf.length)) -eq $buf.length){
$total += $buf.length
$md5.TransformBlock($buf,$offset,$buf.length,$buf,$offset)
Write-Progress -Activity "Hashing File" `
-Status $file -percentComplete ($total/$fd.length * 100)
}
# Finalize the last read
$md5.TransformFinalBlock($buf, 0, $read_len)
$hash = $md5.Hash
# Convert hash bytes to a hexadecimal formatted string
$hash | foreach { $hash_txt += $_.ToString("x2") }
Write-Host $hash_txt
write-progress
linii? Podświetlaczowi składni chyba się to nie podoba.
W tej witrynie znajduje się przykład: Używanie programu PowerShell dla sum kontrolnych MD5 . Używa platformy .NET do utworzenia wystąpienia algorytmu wyznaczania wartości skrótu MD5 w celu obliczenia wartości skrótu.
Oto kod z artykułu, zawierający komentarz Stephena:
param
(
$file
)
$algo = [System.Security.Cryptography.HashAlgorithm]::Create("MD5")
$stream = New-Object System.IO.FileStream($Path, [System.IO.FileMode]::Open,
[System.IO.FileAccess]::Read)
$md5StringBuilder = New-Object System.Text.StringBuilder
$algo.ComputeHash($stream) | % { [void] $md5StringBuilder.Append($_.ToString("x2")) }
$md5StringBuilder.ToString()
$stream.Dispose()
Jak stwierdzono w przyjętej odpowiedzi, Get-FileHash
jest łatwy w użyciu z plikami, ale można go również używać z ciągami:
$s = "asdf"
Get-FileHash -InputStream ([System.IO.MemoryStream]::New([System.Text.Encoding]::ASCII.GetBytes($s)))
Jest teraz bardzo przydatna funkcja Get-FileHash.
PS C:\> Get-FileHash C:\Users\Andris\Downloads\Contoso8_1_ENT.iso -Algorithm SHA384 | Format-List
Algorithm : SHA384
Hash : 20AB1C2EE19FC96A7C66E33917D191A24E3CE9DAC99DB7C786ACCE31E559144FEAFC695C58E508E2EBBC9D3C96F21FA3
Path : C:\Users\Andris\Downloads\Contoso8_1_ENT.iso
Po prostu zmień SHA384
naMD5
.
Przykład pochodzi z oficjalnej dokumentacji programu PowerShell 5.1 . Dokumentacja zawiera więcej przykładów.
Staje się to jednolinijkowe, jeśli pobierzesz narzędzie File Checksum Integrity Verifier (FCIV) z firmy Microsoft.
Pobrałem FCIV stąd: Dostępność i opis narzędzia File Checksum Integrity Verifier
Uruchom następujące polecenie. Miałem dziesięć plików do sprawdzenia.
Get-ChildItem WTAM*.tar | % {.\fciv $_.Name}
Jednoliniowe PowerShell (ciąg do skrótu)
MD5
([System.BitConverter]::ToString((New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider).ComputeHash((New-Object -TypeName System.Text.UTF8Encoding).GetBytes("Hello, World!")))).Replace("-","")
SHA1
([System.BitConverter]::ToString((New-Object -TypeName System.Security.Cryptography.SHA1CryptoServiceProvider).ComputeHash((New-Object -TypeName System.Text.UTF8Encoding).GetBytes("Hello, World!")))).Replace("-","")
SHA256
([System.BitConverter]::ToString((New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider).ComputeHash((New-Object -TypeName System.Text.UTF8Encoding).GetBytes("Hello, World!")))).Replace("-","")
SHA384
([System.BitConverter]::ToString((New-Object -TypeName System.Security.Cryptography.SHA384CryptoServiceProvider).ComputeHash((New-Object -TypeName System.Text.UTF8Encoding).GetBytes("Hello, World!")))).Replace("-","")
SHA512
([System.BitConverter]::ToString((New-Object -TypeName System.Security.Cryptography.SHA512CryptoServiceProvider).ComputeHash((New-Object -TypeName System.Text.UTF8Encoding).GetBytes("Hello, World!")))).Replace("-","")
Spowoduje to zwrócenie skrótu MD5 dla pliku na komputerze zdalnym:
Invoke-Command -ComputerName RemoteComputerName -ScriptBlock {
$fullPath = Resolve-Path 'c:\Program Files\Internet Explorer\iexplore.exe'
$md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
$file = [System.IO.File]::OpenRead($fullPath)
$hash = [System.BitConverter]::ToString($md5.ComputeHash($file))
$hash -replace "-", ""
$file.Dispose()
}
Oto ładny przykład wydruku próbujący zweryfikować odcisk palca SHA256. Pobrałem gpg4win v3.0.3 przy użyciu PowerShell v4 (wymaga Get-FileHash
).
Pobierz pakiet z https://www.gpg4win.org/download.html , otwórz PowerShell, pobierz hash ze strony pobierania i uruchom:
cd ${env:USERPROFILE}\Downloads
$file = "gpg4win-3.0.3.exe"
# Set $hash to the hash reference from the download page:
$hash = "477f56212ee60cc74e0c5e5cc526cec52a069abff485c89c2d57d1b4b6a54971"
# If you have an MD5 hash: # $hashAlgo="MD5"
$hashAlgo = "SHA256"
$computed_hash = (Get-FileHash -Algorithm $hashAlgo $file).Hash.ToUpper()
if ($computed_hash.CompareTo($hash.ToUpper()) -eq 0 ) {
Write-Output "Hash matches for file $file"
}
else {
Write-Output ("Hash DOES NOT match for file {0}: `nOriginal hash: {1} `nComputed hash: {2}" -f ($file, $hash.ToUpper(), $computed_hash))
}
Wynik:
Hash matches for file gpg4win-3.0.3.exe
Oto przykład jednego wiersza polecenia, w którym oba obliczają odpowiednią sumę kontrolną pliku , tak jak właśnie pobrałeś, i porównujesz ją z opublikowaną sumą kontrolną oryginału.
Na przykład napisałem przykład do pobrania z projektu Apache JMeter . W tym przypadku masz:
3a84491f10fb7b147101cf3926c4a855 * apache-jmeter-4.0.zip
Następnie za pomocą tego polecenia PowerShell możesz zweryfikować integralność pobranego pliku:
PS C:\Distr> (Get-FileHash .\apache-jmeter-4.0.zip -Algorithm MD5).Hash -eq (Get-Content .\apache-jmeter-4.0.zip.md5 | Convert-String -Example "hash path=hash")
Wynik:
True
Wyjaśnienie:
Pierwszy operand -eq
operatora jest wynikiem obliczenia sumy kontrolnej pliku:
(Get-FileHash .\apache-jmeter-4.0.zip -Algorithm MD5).Hash
Drugi operand to opublikowana wartość sumy kontrolnej. Najpierw pobieramy zawartość pliku.md5, który jest jednym ciągiem, a następnie wyodrębniamy wartość skrótu na podstawie formatu ciągu:
Get-Content .\apache-jmeter-4.0.zip.md5 | Convert-String -Example "hash path=hash"
Aby to polecenie działało, zarówno plik, jak i plik.md5 muszą znajdować się w tym samym folderze.
Oto, czego używam, aby uzyskać spójną wartość skrótu:
function New-CrcTable {
[uint32]$c = $null
$crcTable = New-Object 'System.Uint32[]' 256
for ($n = 0; $n -lt 256; $n++) {
$c = [uint32]$n
for ($k = 0; $k -lt 8; $k++) {
if ($c -band 1) {
$c = (0xEDB88320 -bxor ($c -shr 1))
}
else {
$c = ($c -shr 1)
}
}
$crcTable[$n] = $c
}
Write-Output $crcTable
}
function Update-Crc ([uint32]$crc, [byte[]]$buffer, [int]$length, $crcTable) {
[uint32]$c = $crc
for ($n = 0; $n -lt $length; $n++) {
$c = ($crcTable[($c -bxor $buffer[$n]) -band 0xFF]) -bxor ($c -shr 8)
}
Write-Output $c
}
function Get-CRC32 {
<#
.SYNOPSIS
Calculate CRC.
.DESCRIPTION
This function calculates the CRC of the input data using the CRC32 algorithm.
.EXAMPLE
Get-CRC32 $data
.EXAMPLE
$data | Get-CRC32
.NOTES
C to PowerShell conversion based on code in https://www.w3.org/TR/PNG/#D-CRCAppendix
Author: Øyvind Kallstad
Date: 06.02.2017
Version: 1.0
.INPUTS
byte[]
.OUTPUTS
uint32
.LINK
https://communary.net/
.LINK
https://www.w3.org/TR/PNG/#D-CRCAppendix
#>
[CmdletBinding()]
param (
# Array of Bytes to use for CRC calculation
[Parameter(Position = 0, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[byte[]]$InputObject
)
$dataArray = @()
$crcTable = New-CrcTable
foreach ($item in $InputObject) {
$dataArray += $item
}
$inputLength = $dataArray.Length
Write-Output ((Update-Crc -crc 0xffffffffL -buffer $dataArray -length $inputLength -crcTable $crcTable) -bxor 0xffffffffL)
}
function GetHash() {
[CmdletBinding()]
param(
[Parameter(Position = 0, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[string]$InputString
)
$bytes = [System.Text.Encoding]::UTF8.GetBytes($InputString)
$hasCode = Get-CRC32 $bytes
$hex = "{0:x}" -f $hasCode
return $hex
}
function Get-FolderHash {
[CmdletBinding()]
param(
[Parameter(Position = 0, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[string]$FolderPath
)
$FolderContent = New-Object System.Collections.ArrayList
Get-ChildItem $FolderPath -Recurse | Where-Object {
if ([System.IO.File]::Exists($_)) {
$FolderContent.AddRange([System.IO.File]::ReadAllBytes($_)) | Out-Null
}
}
$hasCode = Get-CRC32 $FolderContent
$hex = "{0:x}" -f $hasCode
return $hex.Substring(0, 8).ToLower()
}
Oto fragment, którego używam, aby uzyskać MD5 dla danego ciągu:
$text = "text goes here..."
$md5 = [Security.Cryptography.MD5CryptoServiceProvider]::new()
$utf8 = [Text.UTF8Encoding]::UTF8
$bytes= $md5.ComputeHash($utf8.GetBytes($text))
$hash = [string]::Concat($bytes.foreach{$_.ToString("x2")})