ダウンロードしたインストーラーやzipファイルが改ざんされていないか確認するとき、配布元が提示するMD5やSHA1のハッシュ値と照合するのが定番の方法です。Windowsには追加インストール不要で使えるcertutilというツールが標準搭載されており、バッチファイルから簡単に呼び出せます。
本記事ではcertutil -hashfileの基本から、ハッシュ値の変数抽出・ダウンロードファイルの自動検証・フォルダ内一括計算・改ざん検知まで、実践で使えるレベルで体系的に解説します。
- certutil -hashfileの基本構文と対応アルゴリズム(MD5・SHA1・SHA256等)
- certutilの出力から純粋なハッシュ値だけを変数に取り出す方法
- ダウンロードファイルのハッシュ値を期待値と自動照合する方法
- フォルダ内の複数ファイルを一括処理してCSVに出力する方法
- ファイルの改ざん検知への応用とPowerShellとの比較
certutil -hashfileの基本構文
certutilはWindowsに標準搭載された証明書管理ツールですが、-hashfileオプションでファイルのハッシュ値計算にも使えます。
rem 書式: certutil -hashfile <ファイルパス> <アルゴリズム> certutil -hashfile "C:\download\setup.exe" MD5 certutil -hashfile "C:\download\setup.exe" SHA1 certutil -hashfile "C:\download\setup.exe" SHA256
C:\> certutil -hashfile "C:\download\setup.exe" SHA256 SHA256 ハッシュ (ファイル C:\download\setup.exe): a3f5d2e1b4c6789012345678abcdef0123456789abcdef0123456789abcdef01 CertUtil: -hashfile コマンドは正常に完了しました。
出力は3行構造になっています。この構造を理解することが、ハッシュ値を変数に取り出す際の重要なポイントです。
| 行 | 内容 | 例 |
|---|---|---|
| 1行目 | アルゴリズム名とファイルパスのヘッダー | SHA256 ハッシュ (ファイル C:\...) |
| 2行目 | ハッシュ値本体(16進数) | a3f5d2e1b4c6... |
| 3行目 | 完了メッセージ | CertUtil: -hashfile コマンドは正常に完了しました。 |
対応アルゴリズムの比較
certutilが対応するアルゴリズムとその特徴を整理します。
| アルゴリズム | ハッシュ長 | 用途 | セキュリティ強度 |
|---|---|---|---|
| MD5 | 128bit(32文字) | 整合性チェック(レガシー互換) | △ 衝突脆弱性あり |
| SHA1 | 160bit(40文字) | 整合性チェック(レガシー互換) | △ 理論的に破られている |
| SHA256 | 256bit(64文字) | ダウンロード検証・現行標準 | ◎ 現在推奨 |
| SHA384 | 384bit(96文字) | 高セキュリティ要件 | ◎ |
| SHA512 | 512bit(128文字) | 高セキュリティ要件 | ◎ |
ハッシュ値を変数に取り出す
certutilの出力3行のうち、必要なのは2行目のハッシュ値だけです。findstrとfor /fを組み合わせて抽出します。
基本的な抽出方法
@echo off
setlocal enabledelayedexpansion
set "FILE=C:\download\setup.exe"
rem findstr /v でヘッダー行と完了メッセージ行を除外
rem "CertUtil" を含む行と "ハッシュ" を含む行を除外する
for /f "tokens=* delims=" %%H in (
'certutil -hashfile "%FILE%" SHA256 ^| findstr /v "CertUtil" ^| findstr /v "SHA256"'
) do set "HASH=%%H"
echo SHA256: %HASH%
上記の方法はシンプルですが、環境によってヘッダー行の文言が変わる可能性があります。より安定した方法として、16進数の行だけを正規表現で抽出する方法を推奨します。
正規表現で16進数行だけを抽出する(推奨)
@echo off
setlocal enabledelayedexpansion
set "FILE=C:\download\setup.exe"
rem findstr /R で16進数のみの行を抽出
rem ^[0-9a-fA-F] で始まる行のみを対象にする
for /f "tokens=* delims=" %%H in (
'certutil -hashfile "%FILE%" SHA256 ^| findstr /R "^[0-9a-fA-F]"'
) do set "SHA256=%%H"
rem MD5も同様に取得
for /f "tokens=* delims=" %%H in (
'certutil -hashfile "%FILE%" MD5 ^| findstr /R "^[0-9a-fA-F]"'
) do set "MD5=%%H"
echo MD5 : %MD5%
echo SHA256 : %SHA256%
MD5 : d41d8cd98f00b204e9800998ecf8427e SHA256 : e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
findstr /R "^[0-9a-fA-F]"は「行頭が16進数文字で始まる行」を抽出します。ハッシュ値は16進数の連続した文字列なので、これでヘッダーと完了メッセージを除外できます。/Rは正規表現モードのオプションです。ダウンロードファイルのハッシュ値を照合する
配布元が提示するハッシュ値と計算値を比較して、ファイルが改ざんされていないか自動確認します。
@echo off
setlocal
set "FILE=C:\download\setup.exe"
rem 配布元が提示するSHA256値(すべて大文字に統一しておく)
set "EXPECTED=A3F5D2E1B4C6789012345678ABCDEF0123456789ABCDEF0123456789ABCDEF01"
rem ファイルが存在するか確認
if not exist "%FILE%" (
echo [ERROR] ファイルが見つかりません: %FILE%
exit /b 1
)
rem ハッシュ値を計算して変数に格納
for /f "tokens=* delims=" %%H in (
'certutil -hashfile "%FILE%" SHA256 ^| findstr /R "^[0-9a-fA-F]"'
) do set "ACTUAL=%%H"
rem /I オプションで大文字小文字を区別せずに比較
if /I "%ACTUAL%"=="%EXPECTED%" (
echo [OK] ハッシュ値が一致しました。ファイルは正常です。
echo 計算値: %ACTUAL%
exit /b 0
) else (
echo [NG] ハッシュ値が一致しません!ファイルが破損または改ざんされている可能性があります。
echo 期待値: %EXPECTED%
echo 計算値: %ACTUAL%
exit /b 1
)
rem 一致した場合 [OK] ハッシュ値が一致しました。ファイルは正常です。 計算値: a3f5d2e1b4c6789012345678abcdef0123456789abcdef0123456789abcdef01 rem 不一致の場合 [NG] ハッシュ値が一致しません!ファイルが破損または改ざんされている可能性があります。 期待値: A3F5D2E1B4C6789012345678ABCDEF0123456789ABCDEF0123456789ABCDEF01 計算値: b8c7a9f0e5d4321098765432fedcba9876543210fedcba9876543210fedcba98
if /Iで大文字小文字を無視した比較を行うか、あらかじめ大文字に統一しておきましょう。フォルダ内のファイルを一括でハッシュ計算してCSVに出力する
配布パッケージ全体や監査対象フォルダのハッシュ値を一括で記録したい場合に使います。
@echo off
setlocal enabledelayedexpansion
set "TARGET_DIR=C:\distribution"
set "ALG=SHA256"
set "OUT_CSV=hash_list_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.csv"
rem CSVヘッダー行を書き出し
echo "ファイルパス","%ALG%" > "%OUT_CSV%"
set COUNT=0
set ERRORS=0
for /r "%TARGET_DIR%" %%F in (*) do (
set "HASH="
for /f "tokens=* delims=" %%H in (
'certutil -hashfile "%%~fF" %ALG% ^| findstr /R "^[0-9a-fA-F]"'
) do set "HASH=%%H"
if defined HASH (
echo "%%~fF","!HASH!" >> "%OUT_CSV%"
set /a COUNT+=1
) else (
echo [WARN] ハッシュ計算失敗: %%~fF
set /a ERRORS+=1
)
)
echo.
echo 完了: %COUNT% 件処理 / %ERRORS% 件失敗
echo 出力: %OUT_CSV%
"ファイルパス","SHA256" "C:\distribution\setup.exe","a3f5d2e1b4c6..." "C:\distribution\readme.txt","e3b0c44298fc..." "C:\distribution\lib\core.dll","7f83b1657ff1..."
ファイルの改ざん検知への応用
定期的にハッシュ値を記録しておき、次回実行時と比較することで、ファイルが変更されたかどうかを検知できます。設定ファイルや実行ファイルの不正な改ざんの早期発見に使えます。
@echo off
setlocal enabledelayedexpansion
set "FILE=C:\app\important_config.ini"
set "BASELINE=C:\app\config_hash_baseline.txt"
rem ===== 現在のハッシュ値を計算 =====
for /f "tokens=* delims=" %%H in (
'certutil -hashfile "%FILE%" SHA256 ^| findstr /R "^[0-9a-fA-F]"'
) do set "CURRENT_HASH=%%H"
rem ===== ベースラインが存在しない場合は初回登録 =====
if not exist "%BASELINE%" (
echo %CURRENT_HASH% > "%BASELINE%"
echo [初回登録] ハッシュ値を記録しました: %CURRENT_HASH%
exit /b 0
)
rem ===== ベースラインと比較 =====
set /p BASELINE_HASH= < "%BASELINE%"
if /I "%CURRENT_HASH%"=="%BASELINE_HASH%" (
echo [正常] ファイルに変更はありません。
) else (
echo [警告] ファイルが変更されました!
echo 登録時: %BASELINE_HASH%
echo 現在値: %CURRENT_HASH%
rem タスクスケジューラから実行する場合はここでメール通知やログ記録も可能
exit /b 1
)
certutil vs PowerShell Get-FileHash の比較
Windows 10以降ではPowerShellのGet-FileHashコマンドレットも使えます。どちらを使うべきか比較します。
| 項目 | certutil | PowerShell Get-FileHash |
|---|---|---|
| 動作環境 | Windows XP〜11(全バージョン) | Windows 10以降(PS v4.0+) |
| 起動速度 | 速い | やや遅い(PS起動コスト) |
| 出力形式 | 人間向けテキスト(要加工) | 構造化オブジェクト(扱いやすい) |
| バッチからの呼び出し | ネイティブ対応 | powershell -Commandが必要 |
| 大文字/小文字 | 小文字で出力 | 大文字で出力 |
| エラー処理 | ERRORLEVEL確認で可能 | Try-Catchで詳細制御可能 |
rem PowerShellからGet-FileHashを呼び出す
powershell -Command "Get-FileHash -Path 'C:\download\setup.exe' -Algorithm SHA256 | Select-Object -ExpandProperty Hash"
rem バッチ変数に代入する場合
for /f %%H in ('powershell -Command "Get-FileHash -Path 'C:\download\setup.exe' -Algorithm SHA256 | Select-Object -ExpandProperty Hash"') do set "HASH=%%H"
よくある落とし穴と対処法
落とし穴①:ハッシュ値に余分なスペースが入る
certutilの出力によっては、ハッシュ値の前後にスペースが含まれる場合があります。変数に取り込む際にdelims=オプションを付けて対処します。
for /f %%H in ('certutil -hashfile "%FILE%" MD5 ^| findstr /R "^[0-9a-fA-F]"') do set "HASH=%%H"
rem ↑ tokens未指定の場合スペース区切りで最初のトークンのみ取得
for /f "tokens=* delims=" %%H in ('certutil -hashfile "%FILE%" MD5 ^| findstr /R "^[0-9a-fA-F]"') do set "HASH=%%H"
rem ↑ "tokens=* delims=" で行全体を1つのトークンとして取得
落とし穴②:ファイルパスにスペースが含まれている
certutil -hashfile C:\My Documents\setup.exe SHA256 rem → 'C:\My' が引数1、'Documents\setup.exe' が引数2と解釈される
certutil -hashfile "C:\My Documents\setup.exe" SHA256
落とし穴③:大きなファイルで時間がかかる
数GBのファイルではハッシュ計算に数分かかることがあります。ネットワークドライブ上のファイルはまずローカルにコピーしてから計算すると速くなります。また処理中に進捗表示はできないため、タイムアウト対策が必要な場面では注意してください。
rem ネットワークドライブ上の大きなファイルは先にコピー
set "REMOTE_FILE=\\server\share\large_backup.zip"
set "LOCAL_COPY=%TEMP%\hash_target.tmp"
echo コピー中...
copy /y "%REMOTE_FILE%" "%LOCAL_COPY%" >nul
if %ERRORLEVEL% neq 0 (
echo [ERROR] コピー失敗
exit /b 1
)
echo ハッシュ計算中...
for /f "tokens=* delims=" %%H in (
'certutil -hashfile "%LOCAL_COPY%" SHA256 ^| findstr /R "^[0-9a-fA-F]"'
) do set "HASH=%%H"
rem 一時ファイルを削除
del /f "%LOCAL_COPY%" >nul 2>&1
echo SHA256: %HASH%
落とし穴④:certutil コマンドが見つからないエラー
certutilはシステムコマンドですが、PATH設定が壊れている環境では見つからないことがあります。フルパスで指定すれば確実に動作します。
rem certutil のフルパス(通常この場所に存在する)
set "CERTUTIL=%SystemRoot%\System32\certutil.exe"
if not exist "%CERTUTIL%" (
echo [ERROR] certutil.exe が見つかりません
exit /b 1
)
"%CERTUTIL%" -hashfile "%FILE%" SHA256
完全な実践テンプレート
ダウンロードファイルの検証に使える、エラー処理付きの完全版テンプレートです。
@echo off
setlocal
rem ===== 設定(ここを変更)=====
set "FILE=C:\download\setup.exe"
set "ALG=SHA256"
rem 配布元から取得したハッシュ値(大文字・小文字どちらでも可)
set "EXPECTED=a3f5d2e1b4c6789012345678abcdef0123456789abcdef0123456789abcdef01"
rem ===== 前提チェック =====
if not exist "%FILE%" (
echo [ERROR] ファイルが見つかりません: %FILE%
exit /b 1
)
rem ===== ハッシュ計算 =====
echo %ALG%ハッシュを計算中: %FILE%
for /f "tokens=* delims=" %%H in (
'certutil -hashfile "%FILE%" %ALG% ^| findstr /R "^[0-9a-fA-F]"'
) do set "ACTUAL=%%H"
if not defined ACTUAL (
echo [ERROR] ハッシュ計算に失敗しました。
exit /b 1
)
rem ===== 照合 =====
echo 期待値: %EXPECTED%
echo 計算値: %ACTUAL%
echo.
if /I "%ACTUAL%"=="%EXPECTED%" (
echo [OK] 検証成功: ファイルは正常です。
exit /b 0
) else (
echo [NG] 検証失敗: ファイルが破損または改ざんされている可能性があります。
exit /b 1
)
関連記事
- 【bat】ファイルの存在を監視するバッチファイル完全ガイド(ファイルの定期監視との組み合わせ)
- 【bat】日付と時間をファイル名に挿入する方法完全ガイド(ハッシュログのファイル名に日付を付与する)
- 【bat】バッチファイルの長時間処理で画面出力を最適化する完全ガイド(一括処理時のログ出力制御)
よくある質問
certutil -hashfile "ファイル名" SHA512で計算できます。Windows Vista以降で対応しており、その他にもSHA1・SHA256・SHA384などが使えます。MD2・MD4・MD5はレガシーアルゴリズムとして対応しています。for /r %%F in (*) doのループで複数ファイルを順番に処理し、結果をCSVに書き出す方法を本記事で紹介しています。ただしcertutilはファイルを1件ずつ処理するため、ファイル数が多い場合は時間がかかります。if /I(大文字小文字を無視)を使うか、あらかじめどちらかに統一してください。PowerShellの(Get-FileHash ...).Hashは大文字で返します。Get-FileHashを使うのが標準的なアプローチです。古い環境(Windows XP/Vista)ではFCSIV.exe(File Checksum Integrity Verifier、Microsoftが無料配布)も選択肢になります。