社内のAD CS(Active Directory 証明サービス)で「テンプレート証明書を自動発行したい」「Windows は PowerShell、Linux や macOS は curl で同じことをしたい」というニーズは多いものです。本記事では、管理者がすぐに実運用へ持ち込めるレベルまで踏み込んで、PowerShell のオンライン登録、certreq.exe を使う方法、そして /certsrv(Web Enrollment)を Web API 的に叩く手順を、安全設計やトラブル対処も含めて詳解します。
結論と全体像
AD CS でテンプレート証明書を自動取得する方法は大きく3パターンあります。要件とクライアント OS に応じて選びます。
| パターン | 主な用途 | プロトコル | クライアント | 長所 | 注意点 |
|---|---|---|---|---|---|
A. PowerShell Get‑Certificate | ドメイン参加 Windows のオンライン登録 | AD ポリシー/Enrollment Web Services | Windows | 最小工数。ストアへ自動格納。企業標準のやり方。 | /certsrv 直接ではない。テンプレート権限が必須。 |
B. certreq.exe(INF/CSR) | 細かな制御、GUI 無しのサーバー自動化 | DCOM/RPC(CA へ直接) | Windows | きめ細かい。テンプレート/SAN 指定が確実。 | ポート要件(RPC)。権限・テンプレート設定依存。 |
C. Web Enrollment を curl/Invoke‑WebRequest で叩く | Linux/WSL/macOS を含むクロスプラットフォーム | HTTPS(/certsrv) | Windows, Linux, macOS | エージェント不要。IWA/Kerberos/NTLM に対応可能。 | HTML ベースの「非公開API」。確実な URL エンコードと認証設定が要る。 |
重要:質問にある certrqma.asp は「高度な証明書要求フォーム」の画面です。プログラムからの直接 POST 先は certfnsh.asp を使います(Mode=newreq を指定)。証明書のダウンロードは certnew.cer または certnew.p7b の GET です。
前提条件(共通)
- CA に目的のテンプレート(例:
WebServer)が公開され、対象アカウント(またはコンピューター)に Enroll(必要なら Autoenroll)権限が付与されていること。 - テンプレートが 「サブジェクト名=要求からの提供」 として設定され、SAN を CSR/属性から受け付ける構成であること(AD から自動構成のテンプレートでは CSR の Subject/SAN が反映されません)。
/certsrvは社内限定で TLS のみ。匿名アクセスは無効化し、IWA(Kerberos/NTLM)などの認証を必須にします。
Windows:PowerShell でオンライン登録(推奨)
ドメイン参加 Windows なら、最短距離は Get‑Certificate です。成功時は指定ストアに自動格納されます。
# 管理者 PowerShell
$Template = 'WebServer'
$DnsName = @('www.example.local','api.example.local')
Get-Certificate `
-Template $Template `
-DnsName $DnsName `
-CertStoreLocation 'Cert:\LocalMachine\My' |
Format-List -Property * # 結果確認
ポイント:
- ドメインと CA/テンプレートの設定が正しければ、追加の URL 指定は不要です。
- クロスフォレスト/ワークグループなどでは、Enrollment Web Services(CEP/CES) を構成してから利用します。
Windows:certreq.exe(INF/CSR)で確実に制御
細部を調整したい、RPC で CA に直接出したい場合は certreq を使います。
$inf = @"
[Version]
Signature="$Windows NT$"
[NewRequest]
Subject = "CN=www.example.local"
KeyLength = 2048
Exportable = TRUE
MachineKeySet = TRUE
KeySpec = 1 ; AT_KEYEXCHANGE
RequestType = PKCS10
HashAlgorithm = sha256
[Extensions]
2.5.29.17 = "{text}"
_continue_ = "dns=www.example.local&dns=api.example.local"
[RequestAttributes]
CertificateTemplate = WebServer
"@
$dir = Join-Path $env:TEMP "adcs"
New-Item -ItemType Directory -Path $dir -Force | Out-Null
$infPath = Join-Path $dir "request.inf"
$reqPath = Join-Path $dir "request.req"
$cerPath = Join-Path $dir "issued.cer"
$inf | Set-Content -Path $infPath -Encoding ascii
certreq -new $infPath $reqPath
# 例:CA 表示名は "CAHOST\Contoso-Root-CA"
certreq -submit -config "CAHOST\CA-NAME" $reqPath $cerPath
certreq -accept $cerPath
certreq -submit でテンプレート権限や発行ポリシーにより「保留(要承認)」になる場合があります。保留時は発行後に再取得(certreq -retrieve など)します。
クロスプラットフォーム:Web Enrollment を Web API 的に叩く
/certsrv は HTML フォームですが、正しくエンコードして POST/GET すれば自動化できます。以下は Linux/macOS/WSL での代表例です(Windows でも同じ curl が使えます)。
手順概要
- OpenSSL で鍵と CSR を作成(SAN を含める)。
certfnsh.aspへMode=newreqで CSR を POST(CertAttribにテンプレート名など)。- 戻りの HTML/Location ヘッダーから ReqID を取り出す。
certnew.cer?ReqID=&Enc=b64で証明書をダウンロード。- 必要に応じて
certnew.p7bでチェーンを取得し PEM に展開。
サンプル:OpenSSL + curl(Kerberos 認証)
# 1) 鍵と CSR(SAN 含む)
openssl req -new -newkey rsa:2048 -nodes \
-keyout server.key -out server.csr \
-subj "/CN=www.example.local" \
-addext "subjectAltName=DNS:www.example.local,DNS:api.example.local"
# 2) Kerberos チケット(ドメイン参加クライアント想定)
# 例: ユーザー [[email protected]](mailto:[email protected]) として取得
kinit [[email protected]](mailto:[email protected])
# 3) CSR を POST(--data-urlencode で安全にエンコード)
CAHOST="ca01.example.local"
TEMPLATE="WebServer"
curl --negotiate -u : --silent --show-error
--cacert /etc/ssl/certs/ca-bundle.crt
--data-urlencode "Mode=newreq"
--data-urlencode "CertRequest=$(cat server.csr)"
--data-urlencode "CertAttrib=CertificateTemplate:${TEMPLATE}"
--data-urlencode "SaveCert=yes"
-D headers.txt
"https://${CAHOST}/certsrv/certfnsh.asp"
-o response.html
# 4) ReqID を抽出(Location ヘッダー優先。無ければ HTML から)
REQID=$(grep -oE 'ReqID=[0-9]+' headers.txt | head -n1 | cut -d= -f2)
if [ -z "$REQID" ]; then
REQID=$(grep -oE 'ReqID=[0-9]+' response.html | head -n1 | cut -d= -f2)
fi
echo "ReqID=$REQID"
# 5) 証明書(Base64)を取得
curl --negotiate -u : --silent --show-error
--cacert /etc/ssl/certs/ca-bundle.crt
-o server.cer
"https://${CAHOST}/certsrv/certnew.cer?ReqID=${REQID}&Enc=b64"
# 6) PEM へ変換しフルチェーンを作る(任意)
openssl x509 -in server.cer -out server.pem -outform PEM
curl --negotiate -u : --silent --show-error
--cacert /etc/ssl/certs/ca-bundle.crt
-o chain.p7b
"https://${CAHOST}/certsrv/certnew.p7b?ReqID=${REQID}&Enc=b64"
openssl pkcs7 -print_certs -in chain.p7b -out chain.pem
cat server.pem chain.pem > fullchain.pem
認証方式の切替:
- Kerberos(推奨):
--negotiate -u :(事前にkinit)。 - NTLM:
--ntlm -u "DOMAIN\user:password"。 - 基本認証:
-u user:password(TLS 必須。原則非推奨)。
PowerShell から Web Enrollment を直接叩く例
$CAHost = 'ca01.example.local'
$Csr = Get-Content -Raw -Path '.\server.csr'
$Body = @{
Mode = 'newreq'
CertRequest = $Csr
CertAttrib = 'CertificateTemplate:WebServer'
SaveCert = 'yes'
}
# IWA(現在の資格情報)で POST
$r = Invoke-WebRequest -Uri "https://$CAHost/certsrv/certfnsh.asp" ` -UseDefaultCredentials -Method Post`
-Body $Body -ContentType 'application/x-www-form-urlencoded' `
-MaximumRedirection 0 -ErrorAction SilentlyContinue
# ReqID は Location ヘッダーまたは HTML から抽出
$location = $r.Headers['Location']
if (-not $location) { $location = ($r.Content | Select-String -Pattern 'ReqID=\d+').Matches.Value | Select-Object -First 1 }
$reqId = [regex]::Match($location, 'ReqID=(\d+)').Groups[1].Value
Invoke-WebRequest -UseDefaultCredentials ` -Uri "https://$CAHost/certsrv/certnew.cer?ReqID=$reqId&Enc=b64"`
-OutFile '.\server.cer'
/certsrv(Web Enrollment)で使う主なエンドポイント
| パス | HTTP | 用途 | 主なパラメータ | 備考 |
|---|---|---|---|---|
/certsrv/certrqma.asp | GET | 高度な証明書要求のUI | ― | フォーム画面。プログラムからは直接 POST しない。 |
/certsrv/certfnsh.asp | POST | CSR 送信・要求作成 | Mode=newreq、CertRequest(PKCS#10)、CertAttrib、SaveCert=yes | 応答 HTML 内や Location に ReqID が含まれる。 |
/certsrv/certnew.cer | GET | 発行済み証明書の取得 | ReqID、Enc=b64(または bin) | 未発行時はエラーや保留画面が返る。 |
/certsrv/certnew.p7b | GET | 証明書+チェーン | ReqID、Enc=b64 | Web/アプリでよく使うフルチェーン作成に便利。 |
テンプレートと属性の指定ノウハウ
- テンプレート名:
CertAttrib=CertificateTemplate:<表示名>とします。表示名(例:WebServer)を使います。 - SAN の指定:テンプレートが「要求から許可」になっていることを確認のうえ、CSR へ含める(OpenSSL の
-addext)か、CertAttribでSAN:dns=...を渡します。
複数属性を一度に渡すときは、URL エンコード済みの 1 つの文字列としてまとめます。curl では --data-urlencode を使えば安全です。
# 例:テンプレートと SAN を同時指定(1 つの CertAttrib)
--data-urlencode "CertAttrib=CertificateTemplate:WebServer SAN:dns=www.example.local&dns=api.example.local"
セキュリティ設計と運用ポイント
- 発行権限を最小化:要求主体(ユーザー/コンピューター)に必要最小のテンプレートだけ Enroll を付与。意図しないテンプレートは公開しない。
- /certsrv の公開範囲:社内セグメントに限定。TLS は必須。ゲートウェイ越しの公開は避ける。
- 認証方式:基本は IWA(Kerberos/NTLM)。資格情報を平文で埋め込む設計は避ける。
- 監査ログ:CA 監査・IIS ログを取り、ReqID・クライアント・時刻を相関できるようにする。
- チェーン配布:中間CA までクライアントに配布(GPO/構成管理)。
エラー時の見立て(早見表)
| 症状/HTTP | 主因 | 対処 |
|---|---|---|
| 401/403 | 認証/権限不足 | ドメイン資格情報で再実行。テンプレートの Enroll 権限、IIS 認証設定を確認。 |
| 200 だが HTML に「保留」 | CA の「要承認」ポリシー | CA 管理者が承認後、同じ ReqID で certnew.cer を再取得。 |
| 「要求がテンプレートに違反」 | SAN/Subject が許可されていない | テンプレートの「要求からの提供」を有効化し、CSR/属性を見直す。 |
| 「鍵使用法が不正」 | KeySpec/EKU の不一致 | テンプレートの EKU と要求の整合を取る(WebServer なら Server Authentication)。 |
| TLS 証明書検証エラー | CA Web のサーバー証明書未信頼 | --cacert/--capath を使い、-k(検証無効化)は避ける。 |
よくある設計のつまずき
- 「Get‑Certificate で /certsrv を直接指定」:これは非対応です。
Get‑Certificateは AD ポリシーまたは Enrollment Web Services と対話します。Web Enrollment を叩きたい場合はInvoke‑WebRequestかcurlを使います。 - CSR に SAN を入れていない:ブラウザや多くのサーバーは SAN を参照します。CN のみでは警告になります。
- テンプレートが「AD から自動構成」:CSR の Subject/SAN が上書き・無視されます。要件に合うテンプレートを別途用意しましょう。
- Web Enrollment を外部公開:攻撃面が広がります。必ず社内限定、WAF/SEG での越境は避けます。
Windows/Linux での自動化スニペット
PowerShell(汎用関数)
function Request-AdcsCertViaWeb {
param(
[Parameter(Mandatory=$true)] [string] $CAHost,
[Parameter(Mandatory=$true)] [string] $Template,
[Parameter(Mandatory=$true)] [string] $CsrPath,
[Parameter(Mandatory=$true)] [string] $OutCer
)
$csr = Get-Content -Raw -Path $CsrPath
$body = @{
Mode = 'newreq'
CertRequest = $csr
CertAttrib = "CertificateTemplate:$Template"
SaveCert = 'yes'
}
$res = Invoke-WebRequest -Uri "https://$CAHost/certsrv/certfnsh.asp" `
-UseDefaultCredentials -Method Post `
-Body $body -ContentType 'application/x-www-form-urlencoded' `
-MaximumRedirection 0 -ErrorAction SilentlyContinue
$loc = $res.Headers['Location']
if (-not $loc) { $loc = ($res.Content | Select-String -Pattern 'ReqID=\d+').Matches.Value | Select-Object -First 1 }
$reqId = [regex]::Match($loc, 'ReqID=(\d+)').Groups[1].Value
Invoke-WebRequest -UseDefaultCredentials ` -Uri "https://$CAHost/certsrv/certnew.cer?ReqID=$reqId&Enc=b64"`
-OutFile $OutCer
}
Bash(関数化)
request_adcs_cert() {
local CAHOST="$1" TEMPLATE="$2" CSR="$3" OUTCER="$4"
curl --negotiate -u : --silent --show-error \
--cacert /etc/ssl/certs/ca-bundle.crt \
--data-urlencode "Mode=newreq" \
--data-urlencode "CertRequest=$(cat "$CSR")" \
--data-urlencode "CertAttrib=CertificateTemplate:${TEMPLATE}" \
--data-urlencode "SaveCert=yes" \
-D headers.txt \
"https://${CAHOST}/certsrv/certfnsh.asp" \
-o response.html
local REQID
REQID=$(grep -oE 'ReqID=[0-9]+' headers.txt | head -n1 | cut -d= -f2)
[ -z "$REQID" ] && REQID=$(grep -oE 'ReqID=[0-9]+' response.html | head -n1 | cut -d= -f2)
curl --negotiate -u : --silent --show-error
--cacert /etc/ssl/certs/ca-bundle.crt
-o "$OUTCER"
"https://${CAHOST}/certsrv/certnew.cer?ReqID=${REQID}&Enc=b64"
}
既存の証明書をエクスポートする
Windows 側で既にストアにある証明書をファイル化したい場合は、以下が便利です。
| 用途 | コマンド | 備考 |
|---|---|---|
| CER/SST(秘密鍵なし) | Export‑Certificate | 公開鍵のみ。配布や信頼ストア用。 |
| PFX(秘密鍵あり) | Export‑PfxCertificate | パスワード保護を強く。 |
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object Subject -like '*CN=www.example.local*' | Select-Object -First 1
Export-Certificate -Cert $cert -FilePath .\server.cer
Export-PfxCertificate -Cert $cert -FilePath .\server.pfx -Password (Read-Host -AsSecureString)
パイプライン導入チェックリスト
- テンプレートの公開・権限(Enroll/Autoenroll)が正しい。
- テンプレートは「要求からの提供」を許可(Subject/SAN)。
/certsrvは IWA 有効、匿名無効、TLS 証明書は信頼済み。- 自動化スクリプトは HTTP ステータスと ReqID の取得/再試行を実装。
- チェーン配布(中間CA)と失効配布(CDP/AIA)の健全性を監視。
- ログ(CA/IIS)を SIEM に集約し、要求の追跡が可能。
まとめ:PowerShell でも Linux でも安全にテンプレート証明書を取得
Windows なら Get‑Certificate(または certreq.exe)、マルチOSなら /certsrv を curl/Invoke‑WebRequest で叩く、という2本立てにしておけば、サーバー構築や更新の自動化は十分に実現できます。肝は「テンプレート設計(要求からの提供)」「正しいエンコード」「認証方式の選択」の3点。これらを押さえれば、PowerShell でも Linux シェルでも、AD CS からテンプレート証明書を確実かつ安全に取得できます。
付録:コマンド早見表
| 目的 | コマンド例 |
|---|---|
| PowerShell でオンライン取得 | Get-Certificate -Template WebServer -DnsName www.example.local -CertStoreLocation Cert:\LocalMachine\My |
| certreq(INF/CSR 生成〜取得) | certreq -new request.inf request.req → certreq -submit -config "CAHOST\CA-NAME" request.req issued.cer |
| curl で CSR 提出 | curl --negotiate -u : --data-urlencode "Mode=newreq" --data-urlencode "CertRequest=$(cat server.csr)" --data-urlencode "CertAttrib=CertificateTemplate:WebServer" --data-urlencode "SaveCert=yes" https://<CA>/certsrv/certfnsh.asp |
| ReqID で CER 取得 | curl --negotiate -u : -o server.cer "https://<CA>/certsrv/certnew.cer?ReqID=42&Enc=b64" |
| P7B(チェーン)取得 | curl --negotiate -u : -o chain.p7b "https://<CA>/certsrv/certnew.p7b?ReqID=42&Enc=b64" |
| PEM へ変換 | openssl x509 -in server.cer -out server.pem -outform PEM/openssl pkcs7 -print_certs -in chain.p7b -out chain.pem |
FAQ(現場でよく聞かれる疑問)
- Q:テンプレート名は表示名か内部名か?
A:Web Enrollment のCertAttrib=CertificateTemplate:には通常「表示名」を使います。 - Q:CSR に SAN を入れるのと CertAttrib で SAN を渡すのはどちらが良い?
A:運用一貫性の観点では CSR に入れる方が明快です(OpenSSL の-addext等)。ただしテンプレート設定により CA 側が上書きする場合があるため、要件に合わせて選びます。 - Q:ReqID が返らない/抽出できない。
A:まず-Dでレスポンスヘッダーを保存し、Locationを確認。それでも無ければ HTML のcertnew.cer?ReqID=リンクを正規表現で抜きます。IIS のカスタムエラーページが返っていないかもチェック。
これで十分:「Windows は Get‑Certificate で最短」「Web API 的にやるなら certfnsh.asp に POST、certnew.cer を GET」という2本立てを押さえれば、PowerShell でも Linux でも、AD CS からテンプレート証明書を確実に取得できます。

コメント