<p>PowerShellでPKI証明書管理</p>
<h2 class="wp-block-heading">導入(問題設定)</h2>
<p>デジタル証明書は、現代のITインフラにおいて信頼の基盤となる重要な要素です。ウェブサーバーのTLS/SSL、コード署名、VPN認証、デバイス認証など、その用途は多岐にわたります。Windows環境では、これらの証明書は「証明書ストア」と呼ばれる安全な場所に保管・管理されています。</p>
<p>GUI(<code>certmgr.msc</code>やMMCスナップイン)を使った証明書管理は直感的ですが、次のような課題があります。
– <strong>自動化の限界</strong>: 定期的な証明書更新や、大量のサーバーへの展開には不向きです。
– <strong>一貫性の欠如</strong>: 手動操作によるミスが発生しやすく、設定の一貫性を保つのが困難です。
– <strong>可視性の低さ</strong>: ストア内の証明書を一括で検索・監査するのが面倒です。</p>
<p>そこでPowerShellの出番です。PowerShellのCertificateプロバイダと関連Cmdletを活用すれば、これらの課題を解決し、証明書管理を効率的かつ堅牢に行うことができます。本記事では、PowerShellによる証明書管理の基本から、その内部動作、落とし穴、そして堅牢な運用に必要な知識まで、マニアックに掘り下げていきます。</p>
<h2 class="wp-block-heading">理論の要点</h2>
<p>PowerShellによる証明書管理を深く理解するためには、WindowsにおけるPKIと証明書ストアの基本的な概念を把握しておく必要があります。</p>
<h3 class="wp-block-heading">1. Windows証明書ストアの構造</h3>
<p>Windowsには、ユーザーごと(<code>CurrentUser</code>)とコンピューターごと(<code>LocalMachine</code>)に異なる証明書ストアが存在します。PowerShellの<code>Cert:</code>プロバイダは、これらのストアへのパスを提供します。</p>
<ul class="wp-block-list">
<li><strong><code>Cert:\CurrentUser</code></strong>: 現在のユーザープロファイルに関連付けられた証明書ストア。</li>
<li><strong><code>Cert:\LocalMachine</code></strong>: コンピューター全体で利用可能な証明書ストア。サービスアカウントやIISサイトなどが利用します。</li>
</ul>
<p>各ストアには、さらに用途に応じたサブストア(コンテナ)があります。
– <strong><code>My</code> (または <code>Personal</code>)</strong>: ユーザーまたはコンピューターが所有する証明書と秘密鍵が格納されます。主にサーバー証明書やクライアント証明書がここに入ります。
– <strong><code>Root</code></strong>: 信頼されたルート証明機関 (CA) の証明書が格納されます。ここにないCAの証明書は、原則として信頼されません。
– <strong><code>TrustedPublisher</code></strong>: 信頼された発行元の証明書が格納されます。コード署名などで利用されます。
– <strong><code>Intermediate Certification Authorities</code></strong>: 中間CAの証明書が格納されます。</p>
<p>証明書の実体は、<strong>X.509</strong>形式のデータとして、通常はレジストリ(<code>HKLM\SOFTWARE\Microsoft\SystemCertificates</code>など)やファイルシステム(秘密鍵)に格納されます。PowerShellはこれらの抽象化されたインターフェースを提供しています。</p>
<h3 class="wp-block-heading">2. デジタル証明書の構成要素と秘密鍵</h3>
<p>デジタル証明書は公開鍵と、その公開鍵の所有者情報を証明するメタデータ(Subject, Issuer, Validity Period, Extended Key Usageなど)から構成されます。</p>
<ul class="wp-block-list">
<li><strong>Subject</strong>: 証明書の所有者(エンティティ)の情報。</li>
<li><strong>Issuer</strong>: 証明書を発行したCAの情報。</li>
<li><strong>Thumbprint</strong>: 証明書のハッシュ値。証明書を一意に識別するための簡潔なIDとしてよく使われます。</li>
<li><strong>Serial Number</strong>: 発行者CAによって一意に割り当てられる番号。</li>
<li><strong>Validity Period</strong>: 証明書の有効期間(<code>NotBefore</code>から<code>NotAfter</code>まで)。</li>
<li><strong>Extended Key Usage (EKU)</strong>: 証明書がどのような用途で利用されるかを示すOID (Object Identifier) のリスト。例: <code>Server Authentication</code> (TLS/SSLサーバー), <code>Client Authentication</code> (TLS/SSLクライアント), <code>Code Signing</code>。</li>
</ul>
<p>証明書とペアになる<strong>秘密鍵</strong>は、高度な機密情報です。Windowsでは、秘密鍵は通常、暗号化サービスプロバイダ (CSP) またはキー記憶プロバイダ (KSP) によって保護された状態で、ファイルシステム(<code>C:\ProgramData\Microsoft\Crypto\</code>以下など)に保存されます。
– <strong>CSP (Cryptographic Service Provider)</strong>: 比較的古いCryptoAPI (CAPI) で利用されるプロバイダ。通常はRSA鍵用です。
– <strong>KSP (Key Storage Provider)</strong>: 新しいCryptography Next Generation (CNG) で利用されるプロバイダ。RSA、ECCなど多様なアルゴリズムをサポートし、より柔軟なセキュリティ機能を提供します。</p>
<p>秘密鍵には、その鍵がエクスポート可能か否かを制御する<strong>エクスポートポリシー (<code>KeyExportPolicy</code>)</strong> が紐づいています。
– <code>NonExportable</code>: 秘密鍵のエクスポートを許可しません。最も安全な設定です。
– <code>Exportable</code>: 秘密鍵のエクスポートを許可します。バックアップや移行時に必要ですが、セキュリティリスクが増大します。
– <code>ExportableEncrypted</code>: 秘密鍵のエクスポートを許可しますが、常に暗号化して保存する必要があります。</p>
<h3 class="wp-block-heading">3. PFX (PKCS#12) ファイル</h3>
<p>PFXファイル(<code>*.pfx</code>または<code>*.p12</code>)は、証明書と対応する秘密鍵を単一のファイルにまとめたものです。通常、パスワードで保護されており、証明書のバックアップ、移行、または複数のシステムへの展開に利用されます。</p>
<h2 class="wp-block-heading">実装(最小→堅牢化)</h2>
<h3 class="wp-block-heading">最小実装</h3>
<h4 class="wp-block-heading">1. ストア内の証明書を検索する</h4>
<p><code>Get-ChildItem</code> Cmdletを<code>Cert:</code>プロバイダに対して使用します。</p>
<pre data-enlighter-language="generic"># ローカルマシンの個人ストアにある証明書をすべて表示
Get-ChildItem -Path Cert:\LocalMachine\My
# 特定のサブジェクト名を持つ証明書を検索
Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Subject -Match "MyWebApp"
# 特定のThumbprintを持つ証明書を検索(Thumbprintは一意なので最も確実な識別子)
$thumbprint = "1A2B3C4D5E6F7A8B9C0D1E2F3A4B5C6D7E8F9A0B" # 例として適当な値を指定
Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Thumbprint -EQ $thumbprint
# 詳細情報を表示
(Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Subject -Match "MyWebApp")[0] | Format-List *
</pre>
<h4 class="wp-block-heading">2. 自己署名証明書を作成する</h4>
<p><code>New-SelfSignedCertificate</code> Cmdletを使用します。</p>
<pre data-enlighter-language="generic"># 最小構成の自己署名証明書を作成(LocalMachine\Myストアに格納)
$cert = New-SelfSignedCertificate -Subject "CN=MySimpleTestCert" -CertStoreLocation Cert:\LocalMachine\My
Write-Host "証明書が作成されました: $($cert.Thumbprint)"
</pre>
<h4 class="wp-block-heading">3. 証明書をPFXファイルとしてエクスポートする</h4>
<p><code>Export-PfxCertificate</code> Cmdletを使用します。秘密鍵のエクスポートポリシーが<code>Exportable</code>である必要があります。</p>
<pre data-enlighter-language="generic"># 前述の自己署名証明書を変数に格納 (ここではThumbprintで取得)
$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Thumbprint -EQ $cert.Thumbprint
# パスワード付きでPFXとしてエクスポート
$password = Read-Host -AsSecureString "PFXパスワードを入力してください"
Export-PfxCertificate -Cert $cert -FilePath "C:\temp\MySimpleTestCert.pfx" -Password $password
Write-Host "証明書が C:\temp\MySimpleTestCert.pfx にエクスポートされました。"
</pre>
<h4 class="wp-block-heading">4. PFXファイルをインポートする</h4>
<p><code>Import-PfxCertificate</code> Cmdletを使用します。</p>
<pre data-enlighter-language="generic"># インポート先のストアパスを指定
$storePath = "Cert:\LocalMachine\My"
# パスワード付きPFXファイルをインポート
$password = Read-Host -AsSecureString "PFXパスワードを入力してください"
Import-PfxCertificate -FilePath "C:\temp\MySimpleTestCert.pfx" -CertStoreLocation $storePath -Password $password
Write-Host "C:\temp\MySimpleTestCert.pfx がストアにインポートされました。"
</pre>
<h4 class="wp-block-heading">5. 証明書を削除する</h4>
<p><code>Remove-Item</code> Cmdletを使用します。</p>
<pre data-enlighter-language="generic"># 削除対象の証明書を取得
$certToRemove = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Subject -Match "MySimpleTestCert"
# 証明書を削除
if ($certToRemove) {
Remove-Item -Path $certToRemove.PSPath
Write-Host "証明書 $($certToRemove.Thumbprint) が削除されました。"
} else {
Write-Host "削除対象の証明書が見つかりませんでした。"
}
</pre>
<h3 class="wp-block-heading">堅牢化</h3>
<p>ここでは、<code>New-SelfSignedCertificate</code> を中心に、よりセキュアで用途に合わせた証明書作成と管理の方法を掘り下げます。</p>
<h4 class="wp-block-heading">1. 高度な自己署名証明書の作成</h4>
<p><code>New-SelfSignedCertificate</code> は多数のパラメータを持ち、セキュリティ要件や用途に合わせてカスタマイズできます。</p>
<figure class="wp-block-table"><table>
<thead>
<tr>
<th style="text-align:left;">パラメータ名</th>
<th style="text-align:left;">説明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"><code>-Subject</code></td>
<td style="text-align:left;">証明書の識別名 (DN)。例: <code>"CN=myapp.contoso.com, OU=IT, O=Contoso"</code>. 必須。</td>
</tr>
<tr>
<td style="text-align:left;"><code>-CertStoreLocation</code></td>
<td style="text-align:left;">証明書を格納するストアのパス。例: <code>"Cert:\LocalMachine\My"</code>. 必須。</td>
</tr>
<tr>
<td style="text-align:left;"><code>-DnsName</code></td>
<td style="text-align:left;">Subject Alternative Name (SAN) のDNS名を指定。ウェブサーバー用途では必須。複数指定可。例: <code>"myapp.contoso.com", "localhost"</code>.</td>
</tr>
<tr>
<td style="text-align:left;"><code>-KeyUsage</code></td>
<td style="text-align:left;">キー用途を指定。例: <code>DigitalSignature</code>, <code>KeyEncipherment</code>, <code>DataEncipherment</code>. 複数指定可。デフォルトは<code>DigitalSignature, KeyEncipherment</code>。</td>
</tr>
<tr>
<td style="text-align:left;"><code>-ExtendedKeyUsage</code></td>
<td style="text-align:left;">拡張キー用途 (EKU) を指定。OIDまたは標準名。例: <code>ServerAuthentication</code>, <code>ClientAuthentication</code>, <code>CodeSigning</code>. 複数指定可。デフォルトは<code>ServerAuthentication</code>。</td>
</tr>
<tr>
<td style="text-align:left;"><code>-KeyLength</code></td>
<td style="text-align:left;">秘密鍵のビット長。例: <code>2048</code>, <code>4096</code>. デフォルトは<code>2048</code>。</td>
</tr>
<tr>
<td style="text-align:left;"><code>-HashAlgorithm</code></td>
<td style="text-align:left;">証明書の署名に使用するハッシュアルゴリズム。例: <code>SHA256</code>, <code>SHA384</code>, <code>SHA512</code>. デフォルトは<code>SHA256</code>。</td>
</tr>
<tr>
<td style="text-align:left;"><code>-NotBefore</code></td>
<td style="text-align:left;">証明書の有効期限開始日時。デフォルトは現在時刻。</td>
</tr>
<tr>
<td style="text-align:left;"><code>-NotAfter</code></td>
<td style="text-align:left;">証明書の有効期限終了日時。デフォルトは発行から1年間。</td>
</tr>
<tr>
<td style="text-align:left;"><code>-KeyExportPolicy</code></td>
<td style="text-align:left;">秘密鍵のエクスポートポリシー。<code>NonExportable</code>, <code>Exportable</code>, <code>ExportableEncrypted</code>。セキュリティ要件に応じて選択。デフォルトは<code>NonExportable</code>。</td>
</tr>
<tr>
<td style="text-align:left;"><code>-KeyAlgorithm</code></td>
<td style="text-align:left;">秘密鍵のアルゴリズム。例: <code>RSA</code>, <code>ECDH_P256</code>, <code>ECDH_P384</code>, <code>ECDH_P521</code>. デフォルトは<code>RSA</code>。</td>
</tr>
<tr>
<td style="text-align:left;"><code>-Provider</code></td>
<td style="text-align:left;">鍵の保存に使用するCSP/KSP。例: <code>"Microsoft Software Key Storage Provider"</code> (CNG/KSP), <code>"Microsoft Enhanced RSA and AES Cryptographic Provider"</code> (CAPI/CSP)。アーキテクチャやセキュリティ要件に応じて選択。PowerShellは64bitだが、CAPIプロバイダは32bit/64bit対応状況がプロバイダによるため注意。</td>
</tr>
<tr>
<td style="text-align:left;"><code>-FriendlyName</code></td>
<td style="text-align:left;">証明書の表示名。MMCなどで見やすい名前を設定できます。</td>
</tr>
</tbody>
</table></figure>
<p><strong>自己署名証明書(サーバー認証用、エクスポート可能)の例:</strong></p>
<pre data-enlighter-language="generic">$domain = "secureapp.internal.contoso.com"
$certPath = "Cert:\LocalMachine\My"
$password = ConvertTo-SecureString "YourSecureP@ssword" -AsPlainText -Force # 本番ではRead-Hostを使用
$cert = New-SelfSignedCertificate `
-Subject "CN=$domain" `
-DnsName $domain, "localhost" `
-FriendlyName "TLS Certificate for $domain" `
-CertStoreLocation $certPath `
-KeyAlgorithm RSA `
-KeyLength 4096 `
-HashAlgorithm SHA512 `
-NotBefore (Get-Date) `
-NotAfter (Get-Date).AddYears(2) `
-KeyUsage DigitalSignature, KeyEncipherment `
-ExtendedKeyUsage ServerAuthentication `
-KeyExportPolicy Exportable ` # ★重要: PFXエクスポートのためにExportableにする
-Provider "Microsoft Software Key Storage Provider" # CNGプロバイダの指定 (モダンな選択)
Write-Host "高機能な証明書が作成されました: $($cert.Thumbprint)"
# 作成した証明書をPFXとしてエクスポート(バックアップ目的など)
Export-PfxCertificate `
-Cert $cert `
-FilePath "C:\temp\$($domain)_$(Get-Date -Format 'yyyyMMdd').pfx" `
-Password $password `
-Force # 既存ファイルがある場合は上書き
Write-Host "証明書がPFXとしてエクスポートされました。"
</pre>
<p><strong><code>KeyExportPolicy Exportable</code></strong> の指定は、<code>Export-PfxCertificate</code>で秘密鍵をエクスポートする際に必須です。この設定がないと、後述の「失敗例」のように秘密鍵付きのエクスポートに失敗します。</p>
<h4 class="wp-block-heading">2. 証明書の詳細プロパティの確認</h4>
<p><code>X509Certificate2</code>オブジェクトのプロパティを深く掘り下げます。</p>
<pre data-enlighter-language="generic">$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Subject -Match "secureapp.internal.contoso.com"
if ($cert) {
Write-Host "Subject: $($cert.Subject)"
Write-Host "Issuer: $($cert.Issuer)"
Write-Host "Thumbprint: $($cert.Thumbprint)"
Write-Host "Valid From: $($cert.NotBefore)"
Write-Host "Valid To: $($cert.NotAfter)"
Write-Host "Has Private Key: $($cert.HasPrivateKey)"
# 拡張情報
$cert.Extensions | ForEach-Object {
Write-Host " Extension: $($_.Oid.FriendlyName) ($($_.Oid.Value))"
if ($_.Oid.FriendlyName -eq "Subject Alternative Name") {
# SubjectAlternativeNameExtensionをキャストして詳細表示
$sanExtension = [System.Security.Cryptography.X509Certificates.X509SubjectAlternativeNameExtension]$_
$sanExtension.EnumerateDnsNames() | ForEach-Object { Write-Host " DNS Name: $_" }
}
if ($_.Oid.FriendlyName -eq "Key Usage") {
# KeyUsageExtensionをキャストして詳細表示
$keyUsageExtension = [System.Security.Cryptography.X509Certificates.X509KeyUsageExtension]$_
Write-Host " Key Usage: $($keyUsageExtension.KeyUsages)"
}
if ($_.Oid.FriendlyName -eq "Enhanced Key Usage") {
# X509EnhancedKeyUsageExtensionをキャストして詳細表示
$ekuExtension = [System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension]$_
$ekuExtension.EnhancedKeyUsages | ForEach-Object { Write-Host " EKU: $($_.FriendlyName) ($($_.Value))" }
}
}
# 秘密鍵情報の詳細 (HasPrivateKeyがTrueの場合のみ)
if ($cert.HasPrivateKey) {
# CNG (KSP) と CAPI (CSP) の判定
if ($cert.PrivateKey -is [System.Security.Cryptography.RSACng] -or $cert.PrivateKey -is [System.Security.Cryptography.ECDiffieHellmanCng]) {
Write-Host " Key Storage Provider (KSP) Details:"
Write-Host " Provider Name: $($cert.PrivateKey.CngKey.Provider.ProviderName)"
Write-Host " Algorithm Group: $($cert.PrivateKey.CngKey.AlgorithmGroup.FriendlyName)"
Write-Host " Key Name: $($cert.PrivateKey.CngKey.KeyName)"
Write-Host " Export Policy: $($cert.PrivateKey.CngKey.ExportPolicy)" # KeyExportPolicyに対応
} elseif ($cert.PrivateKey -is [System.Security.Cryptography.RSACryptoServiceProvider]) {
Write-Host " Cryptographic Service Provider (CSP) Details:"
Write-Host " Provider Name: $($cert.PrivateKey.CspKeyContainerInfo.ProviderName)"
Write-Host " Key Container Name: $($cert.PrivateKey.CspKeyContainerInfo.KeyContainerName)"
Write-Host " Exportable: $($cert.PrivateKey.CspKeyContainerInfo.Exportable)"
}
}
} else {
Write-Host "証明書が見つかりませんでした。"
}
</pre>
<p><code>X509Certificate2.PrivateKey</code>プロパティは、内部的には.NETの<code>System.Security.Cryptography</code>名前空間のオブジェクト(<code>RSACryptoServiceProvider</code>または<code>RSACng</code>など)を返します。これにより、鍵がCAPI由来かCNG由来かを判別し、それぞれのプロバイダ情報を参照できます。64bit/32bitのアーキテクチャの違いは、これらのプロバイダ実装の選択に影響しますが、PowerShell Cmdletでは抽象化されているため直接意識する必要はほとんどありません。しかし、<strong>CNG (<code>Microsoft Software Key Storage Provider</code>など)</strong> を使用することは、最新のセキュリティ機能と64bit環境での最適なパフォーマンスを得る上で推奨されます。</p>
<h4 class="wp-block-heading">Mermaid図: 証明書とPFXのライフサイクル</h4>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph TD
A["証明書要件定義"] --> B["New-SelfSignedCertificateまたはCAから取得"];
B -- 発行/生成 --> C{"証明書ストアに格納"};
C --> D["秘密鍵の有無とエクスポートポリシー確認"];
D -- 秘密鍵なし または 非エクスポート可能 --> E["証明書単独の利用 (例: 公開鍵の配布)"];
D -- 秘密鍵あり かつ エクスポート可能 --> F[Export-PfxCertificate];
F --> G{"PFXファイル (証明書+秘密鍵) 生成"};
G --> H["PFXファイルを安全に保管/配布"];
H --> I[Import-PfxCertificate];
I --> J["別のシステム/ストアへインポート"];
J --> K["アプリケーションで利用 (例: WebサーバーのTLS)"];
K --> L["証明書有効期限切れ"];
L --> M["更新/再発行"];
M --> B;
C --> N["Remove-Item(\"不要な証明書の削除\")"];
N --> O["ストアから削除完了"];
</pre></div>
<h3 class="wp-block-heading">API仕様・引数・定数一覧</h3>
<p>ここでは、主要なCmdletの重要パラメータと、それに紐づく定数や概念を箇条書きの表形式で示します。</p>
<h4 class="wp-block-heading"><code>New-SelfSignedCertificate</code></h4>
<ul class="wp-block-list">
<li><strong><code>-Subject <String></code></strong>:
<ul>
<li>必須。例: <code>"CN=test.example.com, OU=IT, O=Example Corp"</code></li>
<li><code>CN</code> (Common Name) は特に重要。</li>
</ul></li>
<li><strong><code>-CertStoreLocation <String></code></strong>:
<ul>
<li>必須。例: <code>"Cert:\LocalMachine\My"</code>, <code>"Cert:\CurrentUser\My"</code></li>
</ul></li>
<li><strong><code>-DnsName <String[]></code></strong>:
<ul>
<li>SAN (Subject Alternative Name) に含めるDNS名。複数指定可能。</li>
<li>ウェブサーバー証明書では必須。例: <code>"www.example.com", "example.com"</code></li>
</ul></li>
<li><strong><code>-KeyUsage <X509KeyUsageFlags></code></strong>:
<ul>
<li><code>DigitalSignature</code>, <code>KeyEncipherment</code>, <code>DataEncipherment</code> など。複数指定はカンマ区切り。</li>
<li>用途に応じて適切なものを選択。</li>
</ul></li>
<li><strong><code>-ExtendedKeyUsage <String[]></code></strong>:
<ul>
<li><code>ServerAuthentication</code>, <code>ClientAuthentication</code>, <code>CodeSigning</code> など。OIDでの指定も可能。</li>
<li><code>ServerAuthentication</code> (1.3.6.1.5.5.7.3.1) はウェブサーバーでTLS/SSLに利用する際に必須。</li>
</ul></li>
<li><strong><code>-KeyExportPolicy <X509KeyStorageFlags></code></strong>:
<ul>
<li><code>NonExportable</code> (デフォルト), <code>Exportable</code>, <code>ExportableEncrypted</code>。</li>
<li><code>Exportable</code>にしないとPFXとして秘密鍵をエクスポートできない。</li>
</ul></li>
<li><strong><code>-Provider <String></code></strong>:
<ul>
<li><code>"Microsoft Software Key Storage Provider"</code> (CNG/KSP) または <code>"Microsoft Enhanced RSA and AES Cryptographic Provider"</code> (CAPI/CSP) など。</li>
<li>推奨はCNGプロバイダ。</li>
</ul></li>
</ul>
<h4 class="wp-block-heading"><code>Export-PfxCertificate</code></h4>
<ul class="wp-block-list">
<li><strong><code>-Cert <X509Certificate2></code></strong>:
<ul>
<li>エクスポートする証明書オブジェクト。<code>Get-ChildItem</code>の出力などをパイプラインで渡す。</li>
</ul></li>
<li><strong><code>-FilePath <String></code></strong>:
<ul>
<li>出力するPFXファイルのパス。例: <code>"C:\backup\cert.pfx"</code></li>
</ul></li>
<li><strong><code>-Password <SecureString></code></strong>:
<ul>
<li>PFXファイルを保護するためのパスワード。<code>ConvertTo-SecureString</code>で生成。</li>
<li>パスワードなしでエクスポートするとセキュリティリスクが高まる。</li>
</ul></li>
</ul>
<h4 class="wp-block-heading"><code>Import-PfxCertificate</code></h4>
<ul class="wp-block-list">
<li><strong><code>-FilePath <String></code></strong>:
<ul>
<li>インポートするPFXファイルのパス。</li>
</ul></li>
<li><strong><code>-CertStoreLocation <String></code></strong>:
<ul>
<li>インポート先の証明書ストアのパス。</li>
</ul></li>
<li><strong><code>-Password <SecureString></code></strong>:
<ul>
<li>PFXファイルがパスワードで保護されている場合に必要。</li>
</ul></li>
<li><strong><code>-ExportableKey</code></strong>:
<ul>
<li>インポートした秘密鍵を再度エクスポート可能にするフラグ。デフォルトでは非エクスポート可能としてインポートされる。セキュリティ上の理由から、通常は指定しない。</li>
</ul></li>
</ul>
<h3 class="wp-block-heading">失敗例→原因→対処</h3>
<p><strong>失敗例:</strong>
秘密鍵付きの自己署名証明書をPFXファイルとしてエクスポートしようとしたが、<code>Export-PfxCertificate</code>がエラーを吐いた。</p>
<pre data-enlighter-language="generic"># 1. 秘密鍵のエクスポートポリシーを`NonExportable`(デフォルト)で証明書を作成
$cert = New-SelfSignedCertificate -Subject "CN=NonExportableTestCert" -CertStoreLocation Cert:\LocalMachine\My
Write-Host "作成した証明書 Thumbprint: $($cert.Thumbprint)"
# 2. この証明書をPFXとしてエクスポートしようとする
$password = ConvertTo-SecureString "P@ssword123!" -AsPlainText -Force
Export-PfxCertificate -Cert $cert -FilePath "C:\temp\nonexportable.pfx" -Password $password
# エラーメッセージ例:
# Export-PfxCertificate : Cannot export the certificate with private key.
# A certificate with a private key required for this operation was not found.
# At line:6 char:1
# + Export-PfxCertificate -Cert $cert -FilePath "C:\temp\nonexportable.pfx" -Passw ...
# + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# + CategoryInfo : NotSpecified: (:) [Export-PfxCertificate], CryptographicException
# + FullyQualifiedErrorId : System.Security.Cryptography.CryptographicException,Microsoft.CertificateServices.Commands.ExportPfxCertificateCommand
</pre>
<p><strong>原因:</strong>
<code>New-SelfSignedCertificate</code> Cmdletのデフォルト動作では、作成される証明書の秘密鍵は<code>NonExportable</code>(エクスポート不可)としてマークされます。これはセキュリティ上の推奨事項であり、秘密鍵が安易にコピーされるのを防ぐためです。しかし、PFXファイルは証明書とその秘密鍵の両方を含むため、秘密鍵がエクスポート不可に設定されていると、<code>Export-PfxCertificate</code>は秘密鍵をファイルに書き出すことができず、エラーとなります。</p>
<p><strong>対処:</strong>
証明書を作成する際に、<code>-KeyExportPolicy Exportable</code> または <code>-KeyExportPolicy ExportableEncrypted</code> パラメータを明示的に指定する必要があります。これにより、秘密鍵がエクスポート可能な状態で作成され、PFXとしてエクスポートできるようになります。</p>
<pre data-enlighter-language="generic"># 1. 秘密鍵のエクスポートポリシーを`Exportable`で証明書を作成
$exportableCert = New-SelfSignedCertificate `
-Subject "CN=ExportableTestCert" `
-CertStoreLocation Cert:\LocalMachine\My `
-KeyExportPolicy Exportable # ★ここが重要!
Write-Host "作成したエクスポート可能証明書 Thumbprint: $($exportableCert.Thumbprint)"
# 2. この証明書をPFXとしてエクスポート
$password = ConvertTo-SecureString "P@ssword123!" -AsPlainText -Force
Export-PfxCertificate -Cert $exportableCert -FilePath "C:\temp\exportable.pfx" -Password $password
Write-Host "秘密鍵付きPFXファイル C:\temp\exportable.pfx が正常にエクスポートされました。"
# 後片付け
Remove-Item -Path $exportableCert.PSPath
</pre>
<p><strong>注意点:</strong> <code>Exportable</code>な秘密鍵は、セキュリティリスクが高まります。PFXファイルを扱う際は厳重なパスワードで保護し、アクセス権を適切に設定してください。</p>
<h2 class="wp-block-heading">ベンチ/検証</h2>
<p>作成した証明書やPFXファイルの整合性、パフォーマンスを検証する観点を示します。</p>
<h3 class="wp-block-heading">1. 証明書の正当性検証</h3>
<ul class="wp-block-list">
<li><strong>有効期限</strong>: <code>Get-Date -gt $cert.NotAfter</code> で期限切れをチェック。</li>
<li><strong>信頼チェーン</strong>: <code>Get-AuthenticodeSignature</code> や <code>(Get-Item Cert:\LocalMachine\My\$thumbprint).Verify()</code> メソッドで、証明書が信頼されたルートCAに連なるか確認。自己署名証明書は通常、信頼チェーンのルート自体なので、<code>Verify()</code>は<code>True</code>を返しますが、システムの信頼されたルートストアにインポートしない限り、他のアプリケーションからは信頼されません。</li>
<li><strong>EKU/KeyUsage</strong>: 意図した用途(Server Authenticationなど)が正しく設定されているか確認。</li>
</ul>
<pre data-enlighter-language="generic">$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object Subject -Match "secureapp.internal.contoso.com"
if ($cert) {
if ($cert.NotAfter -lt (Get-Date)) {
Write-Warning "証明書 $($cert.Thumbprint) は期限切れです。"
}
# 証明書の検証 (信頼チェーン、有効期限など)
$isValid = $cert.Verify() # 結果はシステムの信頼設定に依存
Write-Host "証明書検証結果 (Verify()): $($isValid)"
}
</pre>
<h3 class="wp-block-heading">2. PFXファイルの整合性</h3>
<ul class="wp-block-list">
<li><strong>インポートテスト</strong>: エクスポートしたPFXファイルを、別のクリーンなストアやマシンにインポートしてみて、正しく証明書と秘密鍵が復元されるか確認。特に秘密鍵の有無。</li>
<li><strong>パスワードテスト</strong>: 正しいパスワードだけでなく、誤ったパスワードでのインポート失敗も確認。</li>
</ul>
<h3 class="wp-block-heading">3. パフォーマンス計測</h3>
<ul class="wp-block-list">
<li><strong>大量の証明書検索</strong>: <code>Measure-Command</code> を使用して、数百〜数千の証明書の中から特定の条件で検索する際の時間を計測。<code>Get-ChildItem -Path Cert:\LocalMachine\My -Recurse</code> は遅いため、<code>Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object ...</code> の方が良い場合が多い。</li>
<li><strong>自己署名証明書生成時間</strong>: 異なる<code>KeyLength</code>や<code>HashAlgorithm</code>で<code>New-SelfSignedCertificate</code>を実行し、処理時間を比較。</li>
</ul>
<pre data-enlighter-language="generic"># 自己署名証明書生成のパフォーマンス計測
Measure-Command {
New-SelfSignedCertificate -Subject "CN=PerformanceTest" -CertStoreLocation Cert:\CurrentUser\My -KeyLength 4096 -HashAlgorithm SHA512
}
# 大量検索の例(事前に多くの証明書を作成しておくか、既存のシステムで実行)
# Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.NotAfter -lt (Get-Date).AddDays(30)}
# Measure-Command { Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.Subject -match "Microsoft"} }
</pre>
<h2 class="wp-block-heading">応用例/代替案</h2>
<h3 class="wp-block-heading">応用例</h3>
<ol class="wp-block-list">
<li><p><strong>IISサイトへの証明書バインド</strong>:
<code>New-SelfSignedCertificate</code>で作成した証明書や、PFXからインポートした証明書をIISサイトにバインドできます。</p>
<pre data-enlighter-language="generic"># IIS WebAdministrationモジュールをインポート
Import-Module WebAdministration
# 対象の証明書(例:Subjectがsecureapp.internal.contoso.com)
$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Subject -Match "secureapp.internal.contoso.com"
if ($cert) {
# IISサイトのバインディングを追加または更新
$siteName = "Default Web Site"
$port = 443
$ip = "0.0.0.0" # すべてのIPアドレス
$hostname = $cert.Subject.Replace("CN=", "") # SANがない場合
# 既存のバインディングを削除 (必要であれば)
Get-WebBinding -Name $siteName -Protocol https -Port $port | ForEach-Object {
if ($_.HostHeader -eq $hostname -or $_.IPAddress -eq $ip) {
Remove-WebBinding -Name $siteName -Protocol https -Port $port -IPAddress $_.IPAddress -HostHeader $_.HostHeader
}
}
# 新しいバインディングを追加
New-WebBinding -Name $siteName -IPAddress $ip -Port $port -HostHeader $hostname -Protocol https
Set-ItemProperty "IIS:\SslBindings\!$($ip)!$($port)!$($hostname)" -Name "SslCertificateHash" -Value $cert.Thumbprint -Force
Set-ItemProperty "IIS:\SslBindings\!$($ip)!$($port)!$($hostname)" -Name "SslCertificateStoreName" -Value "My"
Write-Host "IISサイト '$siteName' に証明書 $($cert.Thumbprint) をバインドしました。"
} else {
Write-Warning "指定された証明書が見つかりませんでした。"
}
</pre></li>
<li><p><strong>証明書ストアの定期監査</strong>:
有効期限間近の証明書や、不要な自己署名証明書を検出するスクリプトを作成できます。</p>
<pre data-enlighter-language="generic"># 期限切れ間近の証明書を検索(30日以内)
$threshold = (Get-Date).AddDays(30)
Get-ChildItem -Path Cert:\LocalMachine\My, Cert:\CurrentUser\My -Recurse | Where-Object {
$_.NotAfter -lt $threshold -and $_.NotAfter -gt (Get-Date)
} | Format-Table Subject, Issuer, NotAfter, Thumbprint -AutoSize
# 期限切れの証明書を検索
Get-ChildItem -Path Cert:\LocalMachine\My, Cert:\CurrentUser\My -Recurse | Where-Object {
$_.NotAfter -lt (Get-Date)
} | Format-Table Subject, Issuer, NotAfter, Thumbprint -AutoSize
</pre></li>
</ol>
<h3 class="wp-block-heading">代替案</h3>
<ol class="wp-block-list">
<li><p><strong><code>certutil.exe</code></strong>:
Windowsに標準搭載されているコマンドラインツールで、証明書の管理、CAとの連携、秘密鍵の診断など、PowerShell Cmdletでは難しい低レベルな操作も可能です。PowerShellと組み合わせて利用することもよくあります。</p>
<pre data-enlighter-language="generic"># certutil で証明書ストアを表示(LocalMachine\My)
certutil -store -v My
# certutil で秘密鍵のアクセス権を診断
# certutil -repairstore My $thumbprint # 秘密鍵のACLを修復するコマンド(注意して使用)
</pre></li>
<li><p><strong>ACMEクライアント (e.g., <code>Posh-ACME</code>, <code>certbot</code>)</strong>:
Let’s EncryptなどのCAから無料で証明書を取得・自動更新するためのPowerShellモジュール (<code>Posh-ACME</code>) や、Pythonベースの<code>certbot</code>などがあります。これらのツールは、証明書管理の大部分を自動化してくれるため、ウェブサーバー証明書の運用には非常に強力です。本記事の範囲外ですが、実運用では強く推奨されます。</p></li>
</ol>
<h2 class="wp-block-heading">まとめ</h2>
<p>PowerShellのCertificateプロバイダと関連Cmdletは、WindowsにおけるPKI証明書管理を自動化し、堅牢化するための強力なツールです。<code>New-SelfSignedCertificate</code>による詳細な証明書作成、<code>Export-PfxCertificate</code>と<code>Import-PfxCertificate</code>による安全なバックアップと移行、そして<code>Get-ChildItem</code>によるストアの監査は、日常的な運用で不可欠な操作となります。</p>
<p>特に、秘密鍵の<strong>エクスポートポリシー</strong>、<strong>CNG/CAPIプロバイダの違い</strong>、そして<strong>Subject Alternative Name (SAN)</strong> の重要性を理解することは、単なるHowToに留まらない、より深い証明書管理の知識につながります。本記事で解説した内部動作や落とし穴への理解を深め、堅牢なPowerShellスクリプトを構築し、証明書管理の自動化を推進してください。</p>
<h2 class="wp-block-heading">運用チェックリスト</h2>
<ul class="wp-block-list">
<li><strong>証明書の有効期限監視</strong>: 定期的に全証明書の有効期限をチェックし、期限切れが近いものがあればアラートを発報するスクリプトを運用していますか?</li>
<li><strong>秘密鍵の保護</strong>: エクスポート可能な秘密鍵を持つPFXファイルは、厳重なパスワードで保護され、アクセス制限された場所に保管されていますか?</li>
<li><strong>ストアの定期監査</strong>: 不要になった証明書(特に期限切れやテスト目的の自己署名証明書)は定期的にストアから削除されていますか?</li>
<li><strong>KeyExportPolicyの適切な設定</strong>: 証明書作成時、秘密鍵が本当にエクスポート可能である必要がある場合のみ<code>Exportable</code>を指定していますか?</li>
<li><strong>秘密鍵のバックアップ</strong>: 重要な証明書(特にCA発行のもの)の秘密鍵付きPFXは、安全な場所にバックアップされていますか?</li>
<li><strong>SANの利用</strong>: ウェブサーバー証明書では、<code>Subject Alternative Name</code> (SAN) が適切に設定されていますか? (CNのみは非推奨です)</li>
<li><strong>プロバイダの選択</strong>: 新規証明書作成時、可能であれば最新のセキュリティ機能とパフォーマンスを持つCNGプロバイダ(例: <code>Microsoft Software Key Storage Provider</code>)を利用していますか?</li>
</ul>
<h2 class="wp-block-heading">参考リンク</h2>
<ul class="wp-block-list">
<li><a href="https://learn.microsoft.com/powershell/module/microsoft.powershell.security/new-selfsignedcertificate">New-SelfSignedCertificate (Microsoft.PowerShell.Security) – PowerShell | Microsoft Learn</a></li>
<li><a href="https://learn.microsoft.com/powershell/module/microsoft.powershell.security/about/about_certificates">About Certificates (PowerShell) – PowerShell | Microsoft Learn</a></li>
</ul>
PowerShellでPKI証明書管理
導入(問題設定)
デジタル証明書は、現代のITインフラにおいて信頼の基盤となる重要な要素です。ウェブサーバーのTLS/SSL、コード署名、VPN認証、デバイス認証など、その用途は多岐にわたります。Windows環境では、これらの証明書は「証明書ストア」と呼ばれる安全な場所に保管・管理されています。
GUI(certmgr.msc
やMMCスナップイン)を使った証明書管理は直感的ですが、次のような課題があります。
– 自動化の限界 : 定期的な証明書更新や、大量のサーバーへの展開には不向きです。
– 一貫性の欠如 : 手動操作によるミスが発生しやすく、設定の一貫性を保つのが困難です。
– 可視性の低さ : ストア内の証明書を一括で検索・監査するのが面倒です。
そこでPowerShellの出番です。PowerShellのCertificateプロバイダと関連Cmdletを活用すれば、これらの課題を解決し、証明書管理を効率的かつ堅牢に行うことができます。本記事では、PowerShellによる証明書管理の基本から、その内部動作、落とし穴、そして堅牢な運用に必要な知識まで、マニアックに掘り下げていきます。
理論の要点
PowerShellによる証明書管理を深く理解するためには、WindowsにおけるPKIと証明書ストアの基本的な概念を把握しておく必要があります。
1. Windows証明書ストアの構造
Windowsには、ユーザーごと(CurrentUser
)とコンピューターごと(LocalMachine
)に異なる証明書ストアが存在します。PowerShellのCert:
プロバイダは、これらのストアへのパスを提供します。
Cert:\CurrentUser
: 現在のユーザープロファイルに関連付けられた証明書ストア。
Cert:\LocalMachine
: コンピューター全体で利用可能な証明書ストア。サービスアカウントやIISサイトなどが利用します。
各ストアには、さらに用途に応じたサブストア(コンテナ)があります。
– My
(または Personal
) : ユーザーまたはコンピューターが所有する証明書と秘密鍵が格納されます。主にサーバー証明書やクライアント証明書がここに入ります。
– Root
: 信頼されたルート証明機関 (CA) の証明書が格納されます。ここにないCAの証明書は、原則として信頼されません。
– TrustedPublisher
: 信頼された発行元の証明書が格納されます。コード署名などで利用されます。
– Intermediate Certification Authorities
: 中間CAの証明書が格納されます。
証明書の実体は、X.509 形式のデータとして、通常はレジストリ(HKLM\SOFTWARE\Microsoft\SystemCertificates
など)やファイルシステム(秘密鍵)に格納されます。PowerShellはこれらの抽象化されたインターフェースを提供しています。
2. デジタル証明書の構成要素と秘密鍵
デジタル証明書は公開鍵と、その公開鍵の所有者情報を証明するメタデータ(Subject, Issuer, Validity Period, Extended Key Usageなど)から構成されます。
Subject : 証明書の所有者(エンティティ)の情報。
Issuer : 証明書を発行したCAの情報。
Thumbprint : 証明書のハッシュ値。証明書を一意に識別するための簡潔なIDとしてよく使われます。
Serial Number : 発行者CAによって一意に割り当てられる番号。
Validity Period : 証明書の有効期間(NotBefore
からNotAfter
まで)。
Extended Key Usage (EKU) : 証明書がどのような用途で利用されるかを示すOID (Object Identifier) のリスト。例: Server Authentication
(TLS/SSLサーバー), Client Authentication
(TLS/SSLクライアント), Code Signing
。
証明書とペアになる秘密鍵 は、高度な機密情報です。Windowsでは、秘密鍵は通常、暗号化サービスプロバイダ (CSP) またはキー記憶プロバイダ (KSP) によって保護された状態で、ファイルシステム(C:\ProgramData\Microsoft\Crypto\
以下など)に保存されます。
– CSP (Cryptographic Service Provider) : 比較的古いCryptoAPI (CAPI) で利用されるプロバイダ。通常はRSA鍵用です。
– KSP (Key Storage Provider) : 新しいCryptography Next Generation (CNG) で利用されるプロバイダ。RSA、ECCなど多様なアルゴリズムをサポートし、より柔軟なセキュリティ機能を提供します。
秘密鍵には、その鍵がエクスポート可能か否かを制御するエクスポートポリシー (KeyExportPolicy
) が紐づいています。
– NonExportable
: 秘密鍵のエクスポートを許可しません。最も安全な設定です。
– Exportable
: 秘密鍵のエクスポートを許可します。バックアップや移行時に必要ですが、セキュリティリスクが増大します。
– ExportableEncrypted
: 秘密鍵のエクスポートを許可しますが、常に暗号化して保存する必要があります。
3. PFX (PKCS#12) ファイル
PFXファイル(*.pfx
または*.p12
)は、証明書と対応する秘密鍵を単一のファイルにまとめたものです。通常、パスワードで保護されており、証明書のバックアップ、移行、または複数のシステムへの展開に利用されます。
実装(最小→堅牢化)
最小実装
1. ストア内の証明書を検索する
Get-ChildItem
CmdletをCert:
プロバイダに対して使用します。
# ローカルマシンの個人ストアにある証明書をすべて表示
Get-ChildItem -Path Cert:\LocalMachine\My
# 特定のサブジェクト名を持つ証明書を検索
Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Subject -Match "MyWebApp"
# 特定のThumbprintを持つ証明書を検索(Thumbprintは一意なので最も確実な識別子)
$thumbprint = "1A2B3C4D5E6F7A8B9C0D1E2F3A4B5C6D7E8F9A0B" # 例として適当な値を指定
Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Thumbprint -EQ $thumbprint
# 詳細情報を表示
(Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Subject -Match "MyWebApp")[0] | Format-List *
2. 自己署名証明書を作成する
New-SelfSignedCertificate
Cmdletを使用します。
# 最小構成の自己署名証明書を作成(LocalMachine\Myストアに格納)
$cert = New-SelfSignedCertificate -Subject "CN=MySimpleTestCert" -CertStoreLocation Cert:\LocalMachine\My
Write-Host "証明書が作成されました: $($cert.Thumbprint)"
3. 証明書をPFXファイルとしてエクスポートする
Export-PfxCertificate
Cmdletを使用します。秘密鍵のエクスポートポリシーがExportable
である必要があります。
# 前述の自己署名証明書を変数に格納 (ここではThumbprintで取得)
$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Thumbprint -EQ $cert.Thumbprint
# パスワード付きでPFXとしてエクスポート
$password = Read-Host -AsSecureString "PFXパスワードを入力してください"
Export-PfxCertificate -Cert $cert -FilePath "C:\temp\MySimpleTestCert.pfx" -Password $password
Write-Host "証明書が C:\temp\MySimpleTestCert.pfx にエクスポートされました。"
4. PFXファイルをインポートする
Import-PfxCertificate
Cmdletを使用します。
# インポート先のストアパスを指定
$storePath = "Cert:\LocalMachine\My"
# パスワード付きPFXファイルをインポート
$password = Read-Host -AsSecureString "PFXパスワードを入力してください"
Import-PfxCertificate -FilePath "C:\temp\MySimpleTestCert.pfx" -CertStoreLocation $storePath -Password $password
Write-Host "C:\temp\MySimpleTestCert.pfx がストアにインポートされました。"
5. 証明書を削除する
Remove-Item
Cmdletを使用します。
# 削除対象の証明書を取得
$certToRemove = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Subject -Match "MySimpleTestCert"
# 証明書を削除
if ($certToRemove) {
Remove-Item -Path $certToRemove.PSPath
Write-Host "証明書 $($certToRemove.Thumbprint) が削除されました。"
} else {
Write-Host "削除対象の証明書が見つかりませんでした。"
}
堅牢化
ここでは、New-SelfSignedCertificate
を中心に、よりセキュアで用途に合わせた証明書作成と管理の方法を掘り下げます。
1. 高度な自己署名証明書の作成
New-SelfSignedCertificate
は多数のパラメータを持ち、セキュリティ要件や用途に合わせてカスタマイズできます。
パラメータ名
説明
-Subject
証明書の識別名 (DN)。例: "CN=myapp.contoso.com, OU=IT, O=Contoso"
. 必須。
-CertStoreLocation
証明書を格納するストアのパス。例: "Cert:\LocalMachine\My"
. 必須。
-DnsName
Subject Alternative Name (SAN) のDNS名を指定。ウェブサーバー用途では必須。複数指定可。例: "myapp.contoso.com", "localhost"
.
-KeyUsage
キー用途を指定。例: DigitalSignature
, KeyEncipherment
, DataEncipherment
. 複数指定可。デフォルトはDigitalSignature, KeyEncipherment
。
-ExtendedKeyUsage
拡張キー用途 (EKU) を指定。OIDまたは標準名。例: ServerAuthentication
, ClientAuthentication
, CodeSigning
. 複数指定可。デフォルトはServerAuthentication
。
-KeyLength
秘密鍵のビット長。例: 2048
, 4096
. デフォルトは2048
。
-HashAlgorithm
証明書の署名に使用するハッシュアルゴリズム。例: SHA256
, SHA384
, SHA512
. デフォルトはSHA256
。
-NotBefore
証明書の有効期限開始日時。デフォルトは現在時刻。
-NotAfter
証明書の有効期限終了日時。デフォルトは発行から1年間。
-KeyExportPolicy
秘密鍵のエクスポートポリシー。NonExportable
, Exportable
, ExportableEncrypted
。セキュリティ要件に応じて選択。デフォルトはNonExportable
。
-KeyAlgorithm
秘密鍵のアルゴリズム。例: RSA
, ECDH_P256
, ECDH_P384
, ECDH_P521
. デフォルトはRSA
。
-Provider
鍵の保存に使用するCSP/KSP。例: "Microsoft Software Key Storage Provider"
(CNG/KSP), "Microsoft Enhanced RSA and AES Cryptographic Provider"
(CAPI/CSP)。アーキテクチャやセキュリティ要件に応じて選択。PowerShellは64bitだが、CAPIプロバイダは32bit/64bit対応状況がプロバイダによるため注意。
-FriendlyName
証明書の表示名。MMCなどで見やすい名前を設定できます。
自己署名証明書(サーバー認証用、エクスポート可能)の例:
$domain = "secureapp.internal.contoso.com"
$certPath = "Cert:\LocalMachine\My"
$password = ConvertTo-SecureString "YourSecureP@ssword" -AsPlainText -Force # 本番ではRead-Hostを使用
$cert = New-SelfSignedCertificate `
-Subject "CN=$domain" `
-DnsName $domain, "localhost" `
-FriendlyName "TLS Certificate for $domain" `
-CertStoreLocation $certPath `
-KeyAlgorithm RSA `
-KeyLength 4096 `
-HashAlgorithm SHA512 `
-NotBefore (Get-Date) `
-NotAfter (Get-Date).AddYears(2) `
-KeyUsage DigitalSignature, KeyEncipherment `
-ExtendedKeyUsage ServerAuthentication `
-KeyExportPolicy Exportable ` # ★重要: PFXエクスポートのためにExportableにする
-Provider "Microsoft Software Key Storage Provider" # CNGプロバイダの指定 (モダンな選択)
Write-Host "高機能な証明書が作成されました: $($cert.Thumbprint)"
# 作成した証明書をPFXとしてエクスポート(バックアップ目的など)
Export-PfxCertificate `
-Cert $cert `
-FilePath "C:\temp\$($domain)_$(Get-Date -Format 'yyyyMMdd').pfx" `
-Password $password `
-Force # 既存ファイルがある場合は上書き
Write-Host "証明書がPFXとしてエクスポートされました。"
KeyExportPolicy Exportable
の指定は、Export-PfxCertificate
で秘密鍵をエクスポートする際に必須です。この設定がないと、後述の「失敗例」のように秘密鍵付きのエクスポートに失敗します。
2. 証明書の詳細プロパティの確認
X509Certificate2
オブジェクトのプロパティを深く掘り下げます。
$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Subject -Match "secureapp.internal.contoso.com"
if ($cert) {
Write-Host "Subject: $($cert.Subject)"
Write-Host "Issuer: $($cert.Issuer)"
Write-Host "Thumbprint: $($cert.Thumbprint)"
Write-Host "Valid From: $($cert.NotBefore)"
Write-Host "Valid To: $($cert.NotAfter)"
Write-Host "Has Private Key: $($cert.HasPrivateKey)"
# 拡張情報
$cert.Extensions | ForEach-Object {
Write-Host " Extension: $($_.Oid.FriendlyName) ($($_.Oid.Value))"
if ($_.Oid.FriendlyName -eq "Subject Alternative Name") {
# SubjectAlternativeNameExtensionをキャストして詳細表示
$sanExtension = [System.Security.Cryptography.X509Certificates.X509SubjectAlternativeNameExtension]$_
$sanExtension.EnumerateDnsNames() | ForEach-Object { Write-Host " DNS Name: $_" }
}
if ($_.Oid.FriendlyName -eq "Key Usage") {
# KeyUsageExtensionをキャストして詳細表示
$keyUsageExtension = [System.Security.Cryptography.X509Certificates.X509KeyUsageExtension]$_
Write-Host " Key Usage: $($keyUsageExtension.KeyUsages)"
}
if ($_.Oid.FriendlyName -eq "Enhanced Key Usage") {
# X509EnhancedKeyUsageExtensionをキャストして詳細表示
$ekuExtension = [System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension]$_
$ekuExtension.EnhancedKeyUsages | ForEach-Object { Write-Host " EKU: $($_.FriendlyName) ($($_.Value))" }
}
}
# 秘密鍵情報の詳細 (HasPrivateKeyがTrueの場合のみ)
if ($cert.HasPrivateKey) {
# CNG (KSP) と CAPI (CSP) の判定
if ($cert.PrivateKey -is [System.Security.Cryptography.RSACng] -or $cert.PrivateKey -is [System.Security.Cryptography.ECDiffieHellmanCng]) {
Write-Host " Key Storage Provider (KSP) Details:"
Write-Host " Provider Name: $($cert.PrivateKey.CngKey.Provider.ProviderName)"
Write-Host " Algorithm Group: $($cert.PrivateKey.CngKey.AlgorithmGroup.FriendlyName)"
Write-Host " Key Name: $($cert.PrivateKey.CngKey.KeyName)"
Write-Host " Export Policy: $($cert.PrivateKey.CngKey.ExportPolicy)" # KeyExportPolicyに対応
} elseif ($cert.PrivateKey -is [System.Security.Cryptography.RSACryptoServiceProvider]) {
Write-Host " Cryptographic Service Provider (CSP) Details:"
Write-Host " Provider Name: $($cert.PrivateKey.CspKeyContainerInfo.ProviderName)"
Write-Host " Key Container Name: $($cert.PrivateKey.CspKeyContainerInfo.KeyContainerName)"
Write-Host " Exportable: $($cert.PrivateKey.CspKeyContainerInfo.Exportable)"
}
}
} else {
Write-Host "証明書が見つかりませんでした。"
}
X509Certificate2.PrivateKey
プロパティは、内部的には.NETのSystem.Security.Cryptography
名前空間のオブジェクト(RSACryptoServiceProvider
またはRSACng
など)を返します。これにより、鍵がCAPI由来かCNG由来かを判別し、それぞれのプロバイダ情報を参照できます。64bit/32bitのアーキテクチャの違いは、これらのプロバイダ実装の選択に影響しますが、PowerShell Cmdletでは抽象化されているため直接意識する必要はほとんどありません。しかし、CNG (Microsoft Software Key Storage Provider
など) を使用することは、最新のセキュリティ機能と64bit環境での最適なパフォーマンスを得る上で推奨されます。
Mermaid図: 証明書とPFXのライフサイクル
graph TD
A["証明書要件定義"] --> B["New-SelfSignedCertificateまたはCAから取得"];
B -- 発行/生成 --> C{"証明書ストアに格納"};
C --> D["秘密鍵の有無とエクスポートポリシー確認"];
D -- 秘密鍵なし または 非エクスポート可能 --> E["証明書単独の利用 (例: 公開鍵の配布)"];
D -- 秘密鍵あり かつ エクスポート可能 --> F[Export-PfxCertificate];
F --> G{"PFXファイル (証明書+秘密鍵) 生成"};
G --> H["PFXファイルを安全に保管/配布"];
H --> I[Import-PfxCertificate];
I --> J["別のシステム/ストアへインポート"];
J --> K["アプリケーションで利用 (例: WebサーバーのTLS)"];
K --> L["証明書有効期限切れ"];
L --> M["更新/再発行"];
M --> B;
C --> N["Remove-Item(\"不要な証明書の削除\")"];
N --> O["ストアから削除完了"];
API仕様・引数・定数一覧
ここでは、主要なCmdletの重要パラメータと、それに紐づく定数や概念を箇条書きの表形式で示します。
New-SelfSignedCertificate
-Subject <String>
:
必須。例: "CN=test.example.com, OU=IT, O=Example Corp"
CN
(Common Name) は特に重要。
-CertStoreLocation <String>
:
必須。例: "Cert:\LocalMachine\My"
, "Cert:\CurrentUser\My"
-DnsName <String[]>
:
SAN (Subject Alternative Name) に含めるDNS名。複数指定可能。
ウェブサーバー証明書では必須。例: "www.example.com", "example.com"
-KeyUsage <X509KeyUsageFlags>
:
DigitalSignature
, KeyEncipherment
, DataEncipherment
など。複数指定はカンマ区切り。
用途に応じて適切なものを選択。
-ExtendedKeyUsage <String[]>
:
ServerAuthentication
, ClientAuthentication
, CodeSigning
など。OIDでの指定も可能。
ServerAuthentication
(1.3.6.1.5.5.7.3.1) はウェブサーバーでTLS/SSLに利用する際に必須。
-KeyExportPolicy <X509KeyStorageFlags>
:
NonExportable
(デフォルト), Exportable
, ExportableEncrypted
。
Exportable
にしないとPFXとして秘密鍵をエクスポートできない。
-Provider <String>
:
"Microsoft Software Key Storage Provider"
(CNG/KSP) または "Microsoft Enhanced RSA and AES Cryptographic Provider"
(CAPI/CSP) など。
推奨はCNGプロバイダ。
Export-PfxCertificate
-Cert <X509Certificate2>
:
エクスポートする証明書オブジェクト。Get-ChildItem
の出力などをパイプラインで渡す。
-FilePath <String>
:
出力するPFXファイルのパス。例: "C:\backup\cert.pfx"
-Password <SecureString>
:
PFXファイルを保護するためのパスワード。ConvertTo-SecureString
で生成。
パスワードなしでエクスポートするとセキュリティリスクが高まる。
Import-PfxCertificate
-FilePath <String>
:
-CertStoreLocation <String>
:
-Password <SecureString>
:
PFXファイルがパスワードで保護されている場合に必要。
-ExportableKey
:
インポートした秘密鍵を再度エクスポート可能にするフラグ。デフォルトでは非エクスポート可能としてインポートされる。セキュリティ上の理由から、通常は指定しない。
失敗例→原因→対処
失敗例:
秘密鍵付きの自己署名証明書をPFXファイルとしてエクスポートしようとしたが、Export-PfxCertificate
がエラーを吐いた。
# 1. 秘密鍵のエクスポートポリシーを`NonExportable`(デフォルト)で証明書を作成
$cert = New-SelfSignedCertificate -Subject "CN=NonExportableTestCert" -CertStoreLocation Cert:\LocalMachine\My
Write-Host "作成した証明書 Thumbprint: $($cert.Thumbprint)"
# 2. この証明書をPFXとしてエクスポートしようとする
$password = ConvertTo-SecureString "P@ssword123!" -AsPlainText -Force
Export-PfxCertificate -Cert $cert -FilePath "C:\temp\nonexportable.pfx" -Password $password
# エラーメッセージ例:
# Export-PfxCertificate : Cannot export the certificate with private key.
# A certificate with a private key required for this operation was not found.
# At line:6 char:1
# + Export-PfxCertificate -Cert $cert -FilePath "C:\temp\nonexportable.pfx" -Passw ...
# + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# + CategoryInfo : NotSpecified: (:) [Export-PfxCertificate], CryptographicException
# + FullyQualifiedErrorId : System.Security.Cryptography.CryptographicException,Microsoft.CertificateServices.Commands.ExportPfxCertificateCommand
原因:
New-SelfSignedCertificate
Cmdletのデフォルト動作では、作成される証明書の秘密鍵はNonExportable
(エクスポート不可)としてマークされます。これはセキュリティ上の推奨事項であり、秘密鍵が安易にコピーされるのを防ぐためです。しかし、PFXファイルは証明書とその秘密鍵の両方を含むため、秘密鍵がエクスポート不可に設定されていると、Export-PfxCertificate
は秘密鍵をファイルに書き出すことができず、エラーとなります。
対処:
証明書を作成する際に、-KeyExportPolicy Exportable
または -KeyExportPolicy ExportableEncrypted
パラメータを明示的に指定する必要があります。これにより、秘密鍵がエクスポート可能な状態で作成され、PFXとしてエクスポートできるようになります。
# 1. 秘密鍵のエクスポートポリシーを`Exportable`で証明書を作成
$exportableCert = New-SelfSignedCertificate `
-Subject "CN=ExportableTestCert" `
-CertStoreLocation Cert:\LocalMachine\My `
-KeyExportPolicy Exportable # ★ここが重要!
Write-Host "作成したエクスポート可能証明書 Thumbprint: $($exportableCert.Thumbprint)"
# 2. この証明書をPFXとしてエクスポート
$password = ConvertTo-SecureString "P@ssword123!" -AsPlainText -Force
Export-PfxCertificate -Cert $exportableCert -FilePath "C:\temp\exportable.pfx" -Password $password
Write-Host "秘密鍵付きPFXファイル C:\temp\exportable.pfx が正常にエクスポートされました。"
# 後片付け
Remove-Item -Path $exportableCert.PSPath
注意点: Exportable
な秘密鍵は、セキュリティリスクが高まります。PFXファイルを扱う際は厳重なパスワードで保護し、アクセス権を適切に設定してください。
ベンチ/検証
作成した証明書やPFXファイルの整合性、パフォーマンスを検証する観点を示します。
1. 証明書の正当性検証
有効期限 : Get-Date -gt $cert.NotAfter
で期限切れをチェック。
信頼チェーン : Get-AuthenticodeSignature
や (Get-Item Cert:\LocalMachine\My\$thumbprint).Verify()
メソッドで、証明書が信頼されたルートCAに連なるか確認。自己署名証明書は通常、信頼チェーンのルート自体なので、Verify()
はTrue
を返しますが、システムの信頼されたルートストアにインポートしない限り、他のアプリケーションからは信頼されません。
EKU/KeyUsage : 意図した用途(Server Authenticationなど)が正しく設定されているか確認。
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object Subject -Match "secureapp.internal.contoso.com"
if ($cert) {
if ($cert.NotAfter -lt (Get-Date)) {
Write-Warning "証明書 $($cert.Thumbprint) は期限切れです。"
}
# 証明書の検証 (信頼チェーン、有効期限など)
$isValid = $cert.Verify() # 結果はシステムの信頼設定に依存
Write-Host "証明書検証結果 (Verify()): $($isValid)"
}
2. PFXファイルの整合性
インポートテスト : エクスポートしたPFXファイルを、別のクリーンなストアやマシンにインポートしてみて、正しく証明書と秘密鍵が復元されるか確認。特に秘密鍵の有無。
パスワードテスト : 正しいパスワードだけでなく、誤ったパスワードでのインポート失敗も確認。
3. パフォーマンス計測
大量の証明書検索 : Measure-Command
を使用して、数百〜数千の証明書の中から特定の条件で検索する際の時間を計測。Get-ChildItem -Path Cert:\LocalMachine\My -Recurse
は遅いため、Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object ...
の方が良い場合が多い。
自己署名証明書生成時間 : 異なるKeyLength
やHashAlgorithm
でNew-SelfSignedCertificate
を実行し、処理時間を比較。
# 自己署名証明書生成のパフォーマンス計測
Measure-Command {
New-SelfSignedCertificate -Subject "CN=PerformanceTest" -CertStoreLocation Cert:\CurrentUser\My -KeyLength 4096 -HashAlgorithm SHA512
}
# 大量検索の例(事前に多くの証明書を作成しておくか、既存のシステムで実行)
# Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.NotAfter -lt (Get-Date).AddDays(30)}
# Measure-Command { Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.Subject -match "Microsoft"} }
応用例/代替案
応用例
IISサイトへの証明書バインド :
New-SelfSignedCertificate
で作成した証明書や、PFXからインポートした証明書をIISサイトにバインドできます。
# IIS WebAdministrationモジュールをインポート
Import-Module WebAdministration
# 対象の証明書(例:Subjectがsecureapp.internal.contoso.com)
$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object Subject -Match "secureapp.internal.contoso.com"
if ($cert) {
# IISサイトのバインディングを追加または更新
$siteName = "Default Web Site"
$port = 443
$ip = "0.0.0.0" # すべてのIPアドレス
$hostname = $cert.Subject.Replace("CN=", "") # SANがない場合
# 既存のバインディングを削除 (必要であれば)
Get-WebBinding -Name $siteName -Protocol https -Port $port | ForEach-Object {
if ($_.HostHeader -eq $hostname -or $_.IPAddress -eq $ip) {
Remove-WebBinding -Name $siteName -Protocol https -Port $port -IPAddress $_.IPAddress -HostHeader $_.HostHeader
}
}
# 新しいバインディングを追加
New-WebBinding -Name $siteName -IPAddress $ip -Port $port -HostHeader $hostname -Protocol https
Set-ItemProperty "IIS:\SslBindings\!$($ip)!$($port)!$($hostname)" -Name "SslCertificateHash" -Value $cert.Thumbprint -Force
Set-ItemProperty "IIS:\SslBindings\!$($ip)!$($port)!$($hostname)" -Name "SslCertificateStoreName" -Value "My"
Write-Host "IISサイト '$siteName' に証明書 $($cert.Thumbprint) をバインドしました。"
} else {
Write-Warning "指定された証明書が見つかりませんでした。"
}
証明書ストアの定期監査 :
有効期限間近の証明書や、不要な自己署名証明書を検出するスクリプトを作成できます。
# 期限切れ間近の証明書を検索(30日以内)
$threshold = (Get-Date).AddDays(30)
Get-ChildItem -Path Cert:\LocalMachine\My, Cert:\CurrentUser\My -Recurse | Where-Object {
$_.NotAfter -lt $threshold -and $_.NotAfter -gt (Get-Date)
} | Format-Table Subject, Issuer, NotAfter, Thumbprint -AutoSize
# 期限切れの証明書を検索
Get-ChildItem -Path Cert:\LocalMachine\My, Cert:\CurrentUser\My -Recurse | Where-Object {
$_.NotAfter -lt (Get-Date)
} | Format-Table Subject, Issuer, NotAfter, Thumbprint -AutoSize
代替案
certutil.exe
:
Windowsに標準搭載されているコマンドラインツールで、証明書の管理、CAとの連携、秘密鍵の診断など、PowerShell Cmdletでは難しい低レベルな操作も可能です。PowerShellと組み合わせて利用することもよくあります。
# certutil で証明書ストアを表示(LocalMachine\My)
certutil -store -v My
# certutil で秘密鍵のアクセス権を診断
# certutil -repairstore My $thumbprint # 秘密鍵のACLを修復するコマンド(注意して使用)
ACMEクライアント (e.g., Posh-ACME
, certbot
) :
Let’s EncryptなどのCAから無料で証明書を取得・自動更新するためのPowerShellモジュール (Posh-ACME
) や、Pythonベースのcertbot
などがあります。これらのツールは、証明書管理の大部分を自動化してくれるため、ウェブサーバー証明書の運用には非常に強力です。本記事の範囲外ですが、実運用では強く推奨されます。
まとめ
PowerShellのCertificateプロバイダと関連Cmdletは、WindowsにおけるPKI証明書管理を自動化し、堅牢化するための強力なツールです。New-SelfSignedCertificate
による詳細な証明書作成、Export-PfxCertificate
とImport-PfxCertificate
による安全なバックアップと移行、そしてGet-ChildItem
によるストアの監査は、日常的な運用で不可欠な操作となります。
特に、秘密鍵のエクスポートポリシー 、CNG/CAPIプロバイダの違い 、そしてSubject Alternative Name (SAN) の重要性を理解することは、単なるHowToに留まらない、より深い証明書管理の知識につながります。本記事で解説した内部動作や落とし穴への理解を深め、堅牢なPowerShellスクリプトを構築し、証明書管理の自動化を推進してください。
運用チェックリスト
証明書の有効期限監視 : 定期的に全証明書の有効期限をチェックし、期限切れが近いものがあればアラートを発報するスクリプトを運用していますか?
秘密鍵の保護 : エクスポート可能な秘密鍵を持つPFXファイルは、厳重なパスワードで保護され、アクセス制限された場所に保管されていますか?
ストアの定期監査 : 不要になった証明書(特に期限切れやテスト目的の自己署名証明書)は定期的にストアから削除されていますか?
KeyExportPolicyの適切な設定 : 証明書作成時、秘密鍵が本当にエクスポート可能である必要がある場合のみExportable
を指定していますか?
秘密鍵のバックアップ : 重要な証明書(特にCA発行のもの)の秘密鍵付きPFXは、安全な場所にバックアップされていますか?
SANの利用 : ウェブサーバー証明書では、Subject Alternative Name
(SAN) が適切に設定されていますか? (CNのみは非推奨です)
プロバイダの選択 : 新規証明書作成時、可能であれば最新のセキュリティ機能とパフォーマンスを持つCNGプロバイダ(例: Microsoft Software Key Storage Provider
)を利用していますか?
参考リンク
コメント