PowerShellで証明書ストアを制御する方法|EKUの用途制限と中間証明書をルートに移動する手順

Windowsの証明書ストアに入っている既定の証明書を「この用途だけに使いたい」「中間ではなくルートとして扱いたい」と感じたことはないでしょうか。本記事では、PowerShellや標準ツールだけを使って、EKU(用途)の考え方とストア間の移動方法、実務での注意点をまとめて解説します。

目次

証明書ストアと「用途(EKU)」の基本を整理する

まずは、そもそも「証明書ストア」と「用途(EKU)」が何を意味しているのかを整理します。ここが曖昧なまま作業を始めると、意図せず署名検証やTLS通信に影響を与えてしまいます。

Windowsの証明書ストアの種類と役割

Windowsには複数の証明書ストアがあり、それぞれ役割とスコープ(コンピューター全体/ユーザー単位)が異なります。代表的なものをPowerShellパスと合わせて一覧にすると次の通りです。

ストア名(日本語UI)論理名PowerShell パス(例)主な用途備考
信頼されたルート証明機関RootCert:\LocalMachine\RootルートCA証明書。最上位の信頼アンカーここに入っている証明書は非常に強い権限を持つ
中間証明機関CACert:\LocalMachine\CA中間CA証明書。ルートとエンドエンティティをつなぐ通常は自動的に補完される
信頼された人 / 信頼された発行元TrustedPeople / TrustedPublisherCert:\LocalMachine\TrustedPublisherコード署名・ドキュメント署名など社内CAや特定ベンダーをここで信頼するケースも
個人MyCert:\CurrentUser\Myユーザー証明書(クライアント認証など)秘密鍵付き証明書が格納される

同じ「Root」「CA」であっても LocalMachine と CurrentUser で分かれる点にも注意が必要です。

スコープPowerShell プレフィックス影響範囲
ローカル コンピューターCert:\LocalMachine\…マシン上の全ユーザー・サービスに影響
現在のユーザーCert:\CurrentUser\…ログオン中のユーザーにのみ影響

この記事で扱う「中間証明書ストア → ルート証明書ストアへの移動」は、基本的に LocalMachine スコープで作業するケース を想定しています。

EKU(Enhanced Key Usage)とは何か

用途(EKU: Enhanced Key Usage)は、「この証明書はどの用途で使ってよいか」を示す属性です。EKUはOID(Object Identifier)で表現され、証明書発行時に埋め込まれます。

よく利用されるEKUを整理すると次のようになります。

用途(EKU)説明OID
サーバー認証HTTPSサーバー証明書(Webサーバー側)1.3.6.1.5.5.7.3.1
クライアント認証クライアント証明書(VPN, Wi-Fi 802.1X, etc)1.3.6.1.5.5.7.3.2
コード署名実行ファイルやドライバーの署名1.3.6.1.5.5.7.3.3
タイムスタンプタイムスタンプトークンの署名1.3.6.1.4.1.311.10.3.2

今回のテーマに近い例として、「このルート証明書はタイムスタンプ用途の署名だけ許可し、それ以外には使わせたくない」といった要求が挙げられます。

EKUは「証明書内部に焼き込まれている」ため後から変更できない

最初の重要ポイントは、EKUそのものは証明書の一部として署名されており、後から書き換えることはできないという事実です。

証明書の構造はざっくりと次のようになっています。

  • Subject(名前)
  • Issuer(発行者)
  • 有効期限
  • 公開鍵
  • 拡張領域(ここにEKUなどが入る)
  • 上記すべてに対する署名

EKUを書き換えるということは、「発行者からの署名対象を改ざんする」という意味になってしまいます。そのため、技術的には「バイナリを書き換える」ことはできても、正当な証明書としては成立しなくなります。

言い換えると、「用途を変えたいなら新しい証明書を再発行する」以外に手段はないというのがPKIの前提です。

ではどうするか。現実的なアプローチは、証明書そのものではなく「OS側の信頼設定」で用途を制限することです。

OS側で「どの用途として信頼するか」を制御する考え方

Windowsでは、証明書に書かれているEKUに加えて、OSやポリシー側で「このルートを何に使ってよいか」をさらに絞り込むという考え方を取ります。

用途制御のイメージを、証明書とOSの関係で整理すると次のようになります。

レイヤー役割変更可否
証明書本体(EKU)「この鍵は何に使えるか」を発行者が定義不可(再発行のみ)
OSの信頼設定「このルート証明書をどんな用途で信頼するか」を管理者が制御可(certutil / GPO / MMC など)

具体的な方法として、以下のような選択肢があります。

  • AD CSサーバーなどで certutil のレジストリ設定を使う
  • グループポリシー(GPO)で信頼されたルート証明書の用途を制限する
  • 単体端末ではMMC(証明書スナップイン)のUIで用途を指定する

それぞれの特徴を簡単に比較すると次の通りです。

方法主な用途メリットデメリット / 注意点
certutil -setreg CA\RestrictedRootCAsAD CS CAサーバーでのルート制限スクリプト化しやすく、自動構成向きCAサーバー向けの設定であり、一般クライアントにはそのまま使えない
グループポリシー(GPO)ドメイン参加端末の一括制御組織全体で統一ポリシーを配布可能ポリシー設計を誤ると全端末に影響が及ぶ
MMC(証明書)個別端末・個別検証GUIでわかりやすく、ピンポイントの調整が可能台数が多い場合は手作業が現実的でない

方法1:certutilによるルート用途制限(CAサーバー向け)

AD CS(Active Directory証明書サービス)のCAサーバーでは、certutil の CA\RestrictedRootCAs レジストリキーを使って、「どのルート証明書をどのEKUに限定するか」を制御できます。

# 現在の制限設定を確認
certutil -getreg CA\RestrictedRootCAs

# 例:拇印(Thumbprint) 0119e8... のルートを
# タイムスタンプ用途(OID: 1.3.6.1.4.1.311.10.3.2)のみに制限
certutil -setreg CA\RestrictedRootCAs +0119e81be9a14cd8e22f40ac118c687ecba3f4d8:1.3.6.1.4.1.311.10.3.2

書式は次のようになっています。

  • +<拇印>:<OID> … 指定したルート証明書の用途をOIDで追加(制限)
  • -<拇印>:<OID> … 指定した用途の制限を削除

ただし、この設定は主にCAサーバー上でのチェーン構築に影響を与えるためのものであり、一般的なクライアントPCで「任意のルートの用途を絞る」という用途には向きません。クライアント側では、次のGPO/MMCを優先的に検討するのが現実的です。

方法2:グループポリシーで用途を制限(組織単位)

ドメイン参加端末やWindows Server環境では、グループポリシーを使って信頼されたルート証明書の用途を制限できます。手順のイメージは次の通りです。

  1. グループポリシー管理コンソール(gpmc.msc)を起動
  2. 対象OUやドメインに新規ポリシーを作成
  3. [コンピューターの構成] → [Windows の設定] → [セキュリティの設定] → [公開キーのポリシー] → [信頼されたルート証明機関] を開く
  4. ここに対象のルート証明書を登録し、プロパティで「証明書の目的」を必要なものだけに制限

この設定を配布すると、クライアント側のローカルストアに「ポリシーによって制御されたルート」としてインポートされ、用途もGPOに合わせて制限されます。

注意点として、Windows 11 Home など一部エディションにはローカルGPOエディタ(gpedit.msc)がありません。その場合は、ドメインGPOから配布するか、後述のMMCでローカルストアを直接管理する運用が現実的です。

方法3:MMC(証明書スナップイン)でローカル用途を制限

単体のPCで用途を限定したい場合、MMCの証明書スナップインからGUI操作で設定できます。

  1. mmc.exe を実行
  2. [ファイル] → [スナップインの追加と削除] → [証明書] を追加
  3. [コンピューター アカウント] → [ローカル コンピューター] を選択
  4. [信頼されたルート証明機関] → [証明書] を開き、対象ルート証明書をダブルクリック
  5. [全般] タブにある「証明書の目的」から用途を指定

このUIで行っている操作を、PowerShellの高レベルAPIだけで完全に再現する手段は現状用意されていません。内部的にはレジストリやポリシーが絡みますが、非推奨な直接編集は将来のバージョンで動作保証がありません。

そのため、「用途制限」に関しては次のような方針が安全です。

  • 組織管理 → GPOで一括制御
  • 検証用途・単体PC → MMCでピンポイントに用途指定
  • スクリプトで完全自動化したい → certutil/GPO設定のテンプレート化を検討

証明書を「中間(CA)→ ルート(Root)」へ移動するPowerShellスクリプト

次に、実際のご質問に近い「中間証明書ストアにある特定の証明書を、ルート証明書ストアに移動したい」という要件を扱います。

例としてよく名前が挙がるのが、Microsoft TPM Root Certificate Authority 2014 のような証明書です。環境によっては中間(CA)ストアに存在し、ルートとして扱いたいケースがあります。

作業の全体フローは次の通りです。

  1. 中間証明書ストアから対象証明書を特定する
  2. ルート証明書ストアに追加する
  3. 必要に応じて中間ストアから削除する(重複排除)
  4. チェーン検証や動作確認を行う

単一証明書をCA → Rootへ移動するサンプル

まずは、特定の拇印(Thumbprint)を指定して、中間ストアからルートストアへ移すシンプルな例です。

$fingerprint = 'd4ffdb19ba590fffaa34db5f4b568706a2978436'  # 例:対象の拇印

# 中間証明書ストア(LocalMachine\CA)から対象を取得
$cert = Get-ChildItem Cert:\LocalMachine\CA |
         Where-Object Thumbprint -eq $fingerprint

if ($cert) {
    # Root ストアへ追加
    $location   = [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
    $rootStore  = New-Object System.Security.Cryptography.X509Certificates.X509Store('Root', $location)
    $rootStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
    $rootStore.Add($cert)
    $rootStore.Close()
    Write-Output "Root ストアへ追加しました。"

    # (任意)CA ストアから削除して重複を解消
    $caStore = New-Object System.Security.Cryptography.X509Certificates.X509Store('CA', $location)
    $caStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
    $caStore.Remove($cert)
    $caStore.Close()
    Write-Output "CA ストアから削除しました。"
}
else {
    Write-Warning "指定の拇印の証明書が LocalMachine\CA に見つかりません。"
}

ポイントは次の通りです。

  • LocalMachine ストアを操作するため、管理者権限の PowerShell で実行する
  • X509Store クラスで Root / CA ストアを ReadWrite で開く
  • 追加後に中間ストアから削除するかどうかは運用ポリシー次第(残しても動作はするが、重複表示される)

より汎用的な関数化の例

同様の作業を複数の証明書に対して行う場合は、関数化しておくと便利です。

function Move-CertificateFromCAtoRoot {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        [string]$Thumbprint,

        [ValidateSet('LocalMachine','CurrentUser')]
        [string]$StoreLocation = 'LocalMachine',

        [switch]$DeleteFromCA
    )

    # 拇印の書式ゆらぎを吸収(スペース削除・大文字化)
    $normalized = ($Thumbprint -replace '\s','').ToUpperInvariant()

    $caPath   = "Cert:\$StoreLocation\CA"
    $rootPath = "Cert:\$StoreLocation\Root"

    Write-Verbose "Searching in $caPath ..."
    $cert = Get-ChildItem $caPath |
            Where-Object { $_.Thumbprint.ToUpperInvariant() -eq $normalized }

    if (-not $cert) {
        Write-Error "Thumbprint [$Thumbprint] の証明書が $caPath に見つかりませんでした。"
        return
    }

    $locationEnum = [System.Security.Cryptography.X509Certificates.StoreLocation]::$StoreLocation

    if ($PSCmdlet.ShouldProcess("$($cert.Subject)", "Add to Root and (optional) remove from CA")) {
        # Root に追加
        $rootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store('Root', $locationEnum)
        $rootStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
        $rootStore.Add($cert)
        $rootStore.Close()
        Write-Output "[$StoreLocation] Root ストアへ追加しました。"

        if ($DeleteFromCA) {
            $caStore = New-Object System.Security.Cryptography.X509Certificates.X509Store('CA', $locationEnum)
            $caStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
            $caStore.Remove($cert)
            $caStore.Close()
            Write-Output "[$StoreLocation] CA ストアから削除しました。"
        }
    }
}

使用例は次のようになります。

# LocalMachine の CA → Root へ移動(CAからは削除しない)
Move-CertificateFromCAtoRoot -Thumbprint 'd4ffdb19ba590fffaa34db5f4b568706a2978436'

# CurrentUser スコープで移動し、CAからも削除
Move-CertificateFromCAtoRoot `
    -Thumbprint 'd4ffdb19ba590fffaa34db5f4b568706a2978436' `
    -StoreLocation CurrentUser `
    -DeleteFromCA `
    -Verbose

この関数は SupportsShouldProcess を有効にしているため、-WhatIf で「実際には変更を行わずにシミュレーションする」こともできます。

Move-CertificateFromCAtoRoot -Thumbprint '...' -WhatIf

移動後の確認コマンド例

実際にRootストアへ移動できたかを確認するには、次のようなコマンドを利用します。

$fp = 'd4ffdb19ba590fffaa34db5f4b568706a2978436'

# Root ストア側の確認
Get-ChildItem Cert:\LocalMachine\Root |
    Where-Object Thumbprint -eq $fp |
    Format-List Subject, Thumbprint, NotBefore, NotAfter

# CA ストア側の残存確認
Get-ChildItem Cert:\LocalMachine\CA |
    Where-Object Thumbprint -eq $fp |
    Format-List Subject, Thumbprint

併せて、チェーン検証が期待通りになっているかを certmgr.msc やアプリケーション側のログで確認しておくと安心です。

「Microsoft TPM Root Certificate Authority 2014」を例にしたケーススタディ

具体的なイメージを持つために、Microsoft TPM Root Certificate Authority 2014 を例に考えてみます(実際に操作する前に、必ず検証環境で試してください)。

対象証明書の存在場所を確認する

PowerShellで対象証明書を探す例です。

# Subject 名に "TPM Root Certificate Authority 2014" を含む証明書をCAストアから検索
Get-ChildItem Cert:\LocalMachine\CA |
    Where-Object { $_.Subject -like '*TPM Root Certificate Authority 2014*' } |
    Select-Object Subject, Thumbprint, NotAfter

ここで得られたThumbprintを、先ほどの Move-CertificateFromCAtoRoot 関数に渡して操作できます。

なぜ中間からルートに移したくなるのか

TPM関連の証明書に限らず、次のようなケースで「ルートに置いてしまいたい」というニーズが出てきます。

  • 特定の組み込みCAをオフライン環境で利用しており、自動ルート更新が効かない
  • 検証環境で意図的にチェーンを簡略化し、デバッグをしやすくしたい
  • サードパーティアプリが「Rootストアにあるルート」しか認識しない実装になっている

ただし、ルートに置いた瞬間にその証明書はシステム全体の信頼の起点になります。想定していない用途にもチェーンが張られ、結果的にセキュリティリスクを広げてしまう可能性がある点には十分注意してください。

移動する前に確認したいチェックリスト

チェック項目内容
必要性本当にRootに移さないと解決しないか?アプリ側設定やGPOで代替できないか?
影響範囲そのルートを使った他の中間証明書・サーバー証明書が存在しないか
ロールバック移動前のストア状態を戻す手段(エクスポートやスナップショット)は用意できているか
ポリシー社内のセキュリティポリシー上、勝手にルートを追加してよい権限か

バックアップとロールバック戦略

証明書ストアの変更は失敗するとかなり厄介なので、変更前のバックアップとロールバック手順を必ず用意しておきましょう。

最低限やっておきたいバックアップ

  • 対象証明書のエクスポート(.cer / .p7b)
  • 変更前のストア一覧をテキストに吐き出す
  • 必要に応じてレジストリ(ポリシー)をエクスポート

例えば、RootストアとCAストアの一覧を取得するには次のような方法があります。

# Root ストア一覧をファイルに保存
Get-ChildItem Cert:\LocalMachine\Root |
    Select-Object Subject, Thumbprint, NotBefore, NotAfter |
    Out-File -FilePath .\RootStore_Before.txt -Encoding UTF8

# CA ストア一覧をファイルに保存
Get-ChildItem Cert:\LocalMachine\CA |
    Select-Object Subject, Thumbprint, NotBefore, NotAfter |
    Out-File -FilePath .\CAStore_Before.txt -Encoding UTF8

GUI派であれば、MMC(証明書スナップイン)から対象証明書を右クリック → [すべてのタスク] → [エクスポート] で .cer ファイルを保存しておくのも有効です。

ロールバックの基本方針

トラブルが発生した場合の戻し方はシンプルです。

  • Rootストアに追加した証明書を削除
  • 必要に応じてCAストアに再度インポート
  • GPOやレジストリで用途制限を入れた場合は、元の値を復元

PowerShellからRootストアの証明書を削除する例は次の通りです。

$fp = 'd4ffdb19ba590fffaa34db5f4b568706a2978436'
$cert = Get-ChildItem Cert:\LocalMachine\Root |
        Where-Object Thumbprint -eq $fp

if ($cert) {
    $location  = [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
    $rootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store('Root', $location)
    $rootStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
    $rootStore.Remove($cert)
    $rootStore.Close()
    Write-Output "Root ストアから削除しました。"
}
else {
    Write-Warning "指定の証明書が Root ストアに見つかりません。"
}

監査とログ出力の工夫(スクリプト実行の見える化)

本番環境で証明書ストアをいじる場合、「誰がいつ何を変えたか」を残しておくことが求められます。PowerShellスクリプトに簡単なログ出力を組み込んでおくと便利です。

Transcriptでログを残す

# セッション全体のログを取得
$logPath = Join-Path -Path (Get-Location) -ChildPath ("CertStoreChange_{0:yyyyMMdd_HHmmss}.log" -f (Get-Date))
Start-Transcript -Path $logPath

# ここに Move-CertificateFromCAtoRoot などの操作を書く

Stop-Transcript
Write-Output "ログファイル: $logPath"

これだけでも、「どの拇印をいつ移動したか」をトレースすることができるようになります。

簡易的なイベントログ出力

もう一歩踏み込むなら、Windowsイベントログに記録する方法もあります。以下は簡易例です。

$source = 'CertStoreMaintenance'

if (-not [System.Diagnostics.EventLog]::SourceExists($source)) {
    New-EventLog -LogName Application -Source $source
}

Write-EventLog -LogName Application `
               -Source $source `
               -EventId 10001 `
               -EntryType Information `
               -Message "証明書の移動を実行しました。Thumbprint=... 実行ユーザー=$env:USERNAME"

組織内で運用するスクリプトであれば、どのイベントIDをどの変更に対応させるかを決めておくと、後から検索がしやすくなります。

実務でのベストプラクティスまとめ

最後に、ここまでの内容を「普段の運用で意識しておきたいポイント」として整理します。

用途(EKU)に関するポイント

  • EKUは証明書発行時に固定され、後から書き換えられない
  • 用途を制限したい場合は、証明書ではなくOS側の信頼設定で制御する
  • 組織配布ならGPO、単体検証ならMMC、CAサーバーならcertutilといった形でレイヤーを使い分ける

中間 → ルートへの移動に関するポイント

  • Rootストアに置いた証明書はシステム全体の信頼アンカーになるため、追加は最小限にとどめる
  • 移動前に、なぜRootに置く必要があるのかを整理し、他の手段で代替できないかを検討する
  • PowerShellの X509Store と Get-ChildItem Cert:\... を組み合わせると、ストア間のコピー・削除は比較的簡単に自動化できる

運用・セキュリティの観点

  • 変更前に必ずバックアップ(証明書エクスポート・ストア一覧の保存・GPOのバックアップ)を取る
  • テスト環境で十分に検証してから本番へ展開する
  • スクリプトにはログ出力(Transcriptやイベントログ)を組み込み、誰がいつ何を実行したかを追跡できるようにしておく
  • Windows 10 / 11 Home 環境ではGPOエディタがないため、MMCやスクリプトベースの運用設計をあらかじめ決めておく

以上のポイントを押さえておくと、「この証明書はここまで」「このルートはこの用途だけ」という粒度で、PowerShellと標準ツールを組み合わせた安全な運用設計がしやすくなります。

WinAPIの直接呼び出しや非公開APIに頼らずとも、「EKUは証明書側」「用途制限はOS側」「ストアの移動はX509Store+スクリプト」という役割分担を理解しておけば、実務で必要なほとんどの調整は十分にカバーできます。あとは、どこまで自動化し、どこからをポリシーやプロセスでカバーするかという、組織ごとの設計の問題になってきます。

この記事を書いた人

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

コメント

コメントする

目次