Jak uzyskać sumę kontrolną MD5 w PowerShell


Odpowiedzi:


326

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-FileHashcmdlet:

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).


12
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.extna Linuksie.
StockB,

Odpowiedź @StockB Keitha poniżej prawdopodobnie poradzi sobie z tym lepiej. Zgadzam się, istnieją pewne wady PowerShell.
vcsjones,

5
Mam zainstalowany waniliowy PowerShell bez rozszerzeń, więc zepsułem i pobrałem zamiast tego klon md5sum wiersza polecenia, który działa świetnie. Chcę lubić rzeczy Microsoftu, ale po prostu nie mogę.
StockB

23
Metoda @StockB vcsjones nie jest buforowana ... = bardzo wymagająca pamięci dla dużych plików. Sugeruję pracę ze strumieniami: $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 .
Davor Josipovic

20
@davor, który utrzymuje strumień otwarty przez nieokreślony czas, więc nie możesz usunąć pliku, dopóki program Powershell nie zostanie zamknięty. $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()
Joe Amenta

57

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

10
Get-Hash pochodzi z rozszerzeń społeczności programu PowerShell. Jeśli nie możesz lub nie możesz użyć pakietu, dodali oni cmdlet Get-FileHashw waniliowym PowerShell 4.0. Vide TechNet .
Tomasz Cudziło

Zauważ, że to (i prawdopodobnie większość rozwiązań PS) koduje łańcuch jako UTF-16 (little-endian?).
Christian Mann

Link do rozszerzeń społeczności programu PowerShell przekierowuje do archiwum CodePlex (program CodePlex został zamknięty w 2017 r.). Może zmienić na GitHub ? (Czy nowa główna lokalizacja jest na GitHubie?)
Peter Mortensen

16

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")

1
Wynik tego nie jest równy wynikowi, jaki otrzymuję z zaakceptowaną odpowiedzią. Oblicza skrót STRING „hello”, a nie PLIKU, który byłby zdefiniowany przez dowolną ścieżkę, którą zastąpię „hello” przez, prawda?
RobertG

1
To prawda, ale OP nie poprosił o plik, a ja przyszedłem tutaj, szukając rozwiązania typu string
Chris F Carroll

16

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.


2
To podejście jest lepsze dla IMHO niż vcsjones i Keith, ponieważ może przyjmować pliki większe niż 2 GB i nie wymaga żadnych rozszerzeń ani PowerShell 4.0.
Chirag Bhatia - chirag64

1
DisposePołączenie powinno być w finallybloku.
jpmc26

12

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ść)


1
To dobra opcja, gdy FipsAlgorithmPolicyjest włączona.
William John Holden

9

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

1
Twoja metoda pokonuje limit 2 GB ReadAllBytes z innych odpowiedzi, co jest dokładnie tym, czego potrzebowałem.
Jay

Co robi lewy przycisk na write-progresslinii? Podświetlaczowi składni chyba się to nie podoba.
mwfearnley

1
@mwfearnley Lewy przycisk umożliwia kontynuację linii. blogs.technet.microsoft.com/heyscriptingguy/2015/06/19/...
cmcginty

6

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()

1
Dobrze, ale nie działa dla plików tylko do odczytu! Potrzebuje $ stream = New-Object System.IO.FileStream ($ Path, [System.IO.FileMode] :: Open, [System.IO.FileAccess] :: Read)
Stephen Connolly

1
Jeśli łącze kiedykolwiek zniknie, odpowiedź będzie zupełnie bezużyteczna. stackoverflow.com/help/how-to-answer
Jestem z Monicą

1
W odpowiedzi na to, co, jak przypuszczam, było twoim głosem przeciw, wyciąłem i wkleiłem kod z artykułu tutaj. Nie zrobiłem tego w zeszłym roku, ponieważ uważałem, że to plagiat. Dodanie adaptacji Stephena tylko do odczytu sprawiło, że poczułem, że warto ją opublikować.
neontapir

@neontapir tylko po to, żeby powiedzieć: opublikowanie czegoś dosłownie (lub z dostosowaniami) jest plagiatem tylko wtedy, gdy nie podajesz źródła. Prawa autorskie (prawne lub moralne) to osobny problem, ale nie martwiłbym się tym w przypadku większości fragmentów kodu.
mwfearnley

6

Jak stwierdzono w przyjętej odpowiedzi, Get-FileHashjest ł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)))

5

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ń SHA384naMD5 .

Przykład pochodzi z oficjalnej dokumentacji programu PowerShell 5.1 . Dokumentacja zawiera więcej przykładów.



3

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("-","")

1

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()
}

1

Przykład również dla opcji menu prawego przycisku myszy:

[HKEY_CLASSES_ROOT\*\shell\SHA1 PS check\command]
@="C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe -NoExit -Command Get-FileHash -Algorithm SHA1 '%1'"

0

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

0

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:

  1. pobrany plik binarny
  2. suma kontrolna oryginału, który jest publikowany w pliku.md5 jako jeden ciąg znaków w formacie:

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 -eqoperatora 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.


0

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()
}

Skąd skopiowałeś kod PowerShell? https://communary.net/ ?
Peter Mortensen

0

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")}) 
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.