PowerShellを使ってファイルのハッシュ値を計算する方法とその応用の手順を先に要約すると「Get-FileHash -Algorithm SHA256で同じbyte列を照合し、期待hashは信頼できる別channelから取得します。hash一致は配布元の真正性を単独では証明しないため、digital signatureやpublisherも確認します。」となります。対象環境は改ざん検知と同一性確認にはSHA-256を既定にし、MD5やSHA1は互換照合に限定する構成です。
期待hashの入手元とalgorithmを確認する
ファイルハッシュ比較では、配布元が指定したアルゴリズムと対象ファイルの完全なパスを先に確認します。Get-FileHashのAlgorithmを明示し、期待値から空白や接頭辞だけを正規化して大文字小文字を無視して比較します。ファイル名、サイズ、更新時刻も併記し、不一致なら実行や展開を止めます。
- Get-ItemでFullName、Length、LastWriteTimeUtcを記録する
- fileが書込中でないことをprocess ownerへ確認する
- 期待hashの入手元とalgorithmを確認する
- symlinkやnetwork pathの実体をResolve-Pathで確認する
SHA-256を64桁hexへ正規化する
計算は読み取りですが、機密fileのhashもidentifierとして扱う場合があります。外部照合siteへfileをuploadせず、公式が提示するhashだけを安全なchannelで比較します。fileが書込中ならprocessを無理に停止せずsnapshotまたはmaintenanceを使います。hash不一致時は実行・配布を止め、正常なsourceから再取得し、疑わしいfileを隔離します。
同一contentと正規publisherを混同しない
SHA-256を計算
Get-FileHash -LiteralPath 'C:\Downloads\package.msi' -Algorithm SHA256 |
Select-Object Algorithm,Hash,Path
既定もSHA256ですが監査手順ではalgorithmを明示します。
期待値と固定時間比較
$expected=('0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' -replace '\s','').ToUpperInvariant()
if($expected -notmatch '^[0-9A-F]{64}$'){ throw 'Expected SHA-256 must be 64 hexadecimal characters' }
$actual=(Get-FileHash -LiteralPath '.\package.msi' -Algorithm SHA256 -ErrorAction Stop).Hash.ToUpperInvariant()
[pscustomobject]@{Algorithm='SHA256';HashMatch=($actual -ceq $expected);Actual=$actual}
期待値は例示文字列のまま使わず公式配布sourceから取得します。formatとlengthを検証してから比較します。
二つのfileを比較
$a=Get-FileHash '.\copy-a.bin' -Algorithm SHA256
$b=Get-FileHash '.\copy-b.bin' -Algorithm SHA256
$a.Hash -eq $b.Hash
一致は同じbyte列である強い根拠ですが、両方が同じ不正fileなら安全性は示しません。
streamのhashを得る
$stream=[IO.File]::OpenRead((Resolve-Path '.\data.bin'))
try { Get-FileHash -InputStream $stream -Algorithm SHA256 } finally { $stream.Dispose() }
streamを確実に閉じます。処理中にfileが変わる可能性があるdataではsnapshotを使います。
署名も確認
Get-AuthenticodeSignature -LiteralPath '.\package.msi' |
Select-Object Status,StatusMessage,SignerCertificate,TimeStamperCertificate
hash照合に加えてsignature statusとpublisherを確認します。UnknownError等をValid扱いしません。
InputStreamはfinallyで必ず閉じる
hashはfile名、timestamp、permissionを含まずbyte列だけに対して計算されます。同じfileをcopyしてmetadataが変わってもhashは一致します。逆に改行コードやencodingが変われば内容が同じ見た目でもhashは変わります。MD5とSHA1は衝突耐性が不十分で、新規のsecurity判断には使いません。
Authenticode署名をhashと別に確認する
- MD5を安全性の証明に使う
- 期待hashを同じ改変可能な場所から取る
- 書込中fileを一回だけhashする
- path違いを見落とす
- hash一致だけでpublisher真正性を断定する
不正な期待値を比較前に拒否する
hashのhex長がalgorithmに合うこと、再計算で一致すること、別copyでも一致することを確認します。公式hash、digital signature、download URL、取得時刻を一組で記録します。不一致の場合は比較文字列の空白・caseだけでなく、file sizeとsource URLを再確認します。
hash一致・署名status・対象pathを検収する
公開されたSHA-256との照合は、期待値の空白を除去して大文字化し、64桁hexか検証してからGet-FileHashのHashとcase-sensitive比較します。FromHexStringとCryptographicOperations.FixedTimeEqualsへ依存せず、Windows PowerShell 5.1でも動く手順にします。
期待SHA-256と実fileを大文字hexで比べる
hash差とfile読取失敗を終了状態で識別する
- SHA-256一致:actualと正規化expectedが64桁で完全一致し、HashMatch=Trueになる
- hash不一致:hash不一致は正常な比較結果としてFalseを返し、fileを実行・置換しない
- 入力・file読取error:file不存在、read error、期待値format不正、計算中変更は例外として比較不一致から分ける
hash一致は発行者や安全性を証明しないため、official HTTPS、署名、versionも確認します。secret比較向けのtiming-safe APIはPowerShell 7/.NET環境差があり、公開checksum照合へ不要に持ち込みません。
短いhexと別algorithmを拒否する
$bad='xyz'
try { if($bad -notmatch '^[0-9A-Fa-f]{64}$'){throw 'InvalidExpectedHash'} } catch { [pscustomobject]@{State='RejectedExpectedHash';Value=$bad} }
一致、不一致、期待値小文字/空白、63/65桁、file変更中、read不可をtestします。size、LastWriteTimeUtcを計算前後に取得し、actual/expected、signature status、source URLを保存します。
hash一致は二つのbyte列が同じことを示しますが、配布元の正当性までは示しません。期待値の公式入手元とAuthenticode statusを別項目で残し、algorithm名も比較記録へ含めます。

コメント