AD CSのテンプレート証明書をPowerShellとcurlで取得する完全ガイド|certsrv Web APIの正しい使い方

社内の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 ServicesWindows最小工数。ストアへ自動格納。企業標準のやり方。/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 が使えます)。

手順概要

  1. OpenSSL で鍵と CSR を作成(SAN を含める)。
  2. certfnsh.asp へ Mode=newreq で CSR を POST(CertAttrib にテンプレート名など)。
  3. 戻りの HTML/Location ヘッダーから ReqID を取り出す。
  4. certnew.cer?ReqID=&Enc=b64 で証明書をダウンロード。
  5. 必要に応じて 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.aspGET高度な証明書要求のUI―フォーム画面。プログラムからは直接 POST しない。
/certsrv/certfnsh.aspPOSTCSR 送信・要求作成Mode=newreq、CertRequest(PKCS#10)、CertAttrib、SaveCert=yes応答 HTML 内や Location に ReqID が含まれる。
/certsrv/certnew.cerGET発行済み証明書の取得ReqID、Enc=b64(または bin)未発行時はエラーや保留画面が返る。
/certsrv/certnew.p7bGET証明書+チェーンReqID、Enc=b64Web/アプリでよく使うフルチェーン作成に便利。

テンプレートと属性の指定ノウハウ

  • テンプレート名: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&amp;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 からテンプレート証明書を確実に取得できます。


この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次