注意
自己責任でお願いします。これら記事の内容を実行して何か不都合を被ったとしても筆者は責任を負いません。
性質上セキュリティホールを自分で開けているようなものなので、実装は実験目的かつ仮想環境を推奨します。また当たり前ですが実装自体には管理者権限が必要です。また自分の所有する端末のみで行ってください。
もし sudo を使いたいなら、Windows 標準 か Grignoli 氏の gsudo を使えばよいと思います。
構成
目次
1. 変数の宣言と代入
2. 接続設定
3. 構成ファイル作成・登録
4. 権限設定
5. 証明書作成
6. 証明書紐付け
7. 実行・関数作成
8. 後始末
説明
LocalMachine の Root 証明書ストアを操作します。十分に注意してください。また最初のクリーンナップ処理(WinRM 用の既存の証明書を削除)は行わなくてもよいです(実験のために証明書を繰り返し作ったため、それの整理用)。
また Microsoft アカウントで PC にサインインしていると Negotiate (Kerberos protocol / NTLM authentication) での自動認証が失敗(Get-Credential を使って手動で認証する分には可)するため自己署名証明書を用いた相互認証を行っています(ユーザー名+パスワードでの認証を、証明書マッピングのタイミングで処理できる)。そのために自己署名証明書を Trusted Root Certification Authorities certificate store (Cert:\LocalMachine\Root) に配置しています。
証明書階層構造は
Root
└ CA Intermediate
├ Server Certificate
└ Client Certificate
で構築しています。
セキュリティ的にテスト端末以外ではやらないほうが良いです。
内容
使用する変数:
$HostName $LocateUser $LocateMy $LocateCA $LocateRoot $Pattern $UserName $LocalUPN
$HostName = "127.0.0.1"
$LocateUser = "Cert:\CurrentUser\My"
$LocateMy = "Cert:\LocalMachine\My"
$LocateCA = "Cert:\LocalMachine\CA"
$LocateRoot = "Cert:\LocalMachine\Root"
$Pattern = "My * - UserDef"
$SelectUser = [Security.Principal.WindowsIdentity]::GetCurrent().Name
$UserObject = [Security.Principal.NTAccount]::new($SelectUser)
$UserName = $UserObject.Value.Split("\")[-1]
$LocalUPN = "$UserName@localhost"
↓すべて管理者権限で実行します↓
# My 証明書を削除(CurrentUser)
$ThumbUserPath = @()
$ThumbprintUser = (Get-ChildItem $LocateUser).Where{$_.FriendlyName -like $Pattern}.Thumbprint -as [PSObject[]]
$ThumbprintUser.ForEach{$ThumbUserPath += Join-Path $LocateUser $_}
Remove-Item $ThumbUserPath -WhatIf
# My 証明書を削除(LocalMachine)
$ThumbMyPath = @()
$ThumbprintMy = (Get-ChildItem $LocateMy).Where{$_.FriendlyName -like $Pattern}.Thumbprint -as [PSObject[]]
$ThumbprintMy.ForEach{$ThumbMyPath += Join-Path $LocateMy $_}
Remove-Item $ThumbMyPath -WhatIf
# CA 証明書を削除(LocalMachine)
$ThumbCAPath = @()
$ThumbprintCA = (Get-ChildItem $LocateCA).Where{$_.FriendlyName -like $Pattern}.Thumbprint -as [PSObject[]]
$ThumbprintCA.ForEach{$ThumbCAPath += Join-Path $LocateCA $_}
Remove-Item $ThumbCAPath -WhatIf
!!!注意!!!
# Root 証明書を削除(LocalMachine)
$ThumbRootPath = @()
$ThumbprintRoot = (Get-ChildItem $LocateRoot).Where{$_.FriendlyName -like $Pattern}.Thumbprint -as [PSObject[]]
$ThumbprintRoot.ForEach{$ThumbRootPath += Join-Path $LocateRoot $_}
Remove-Item $ThumbRootPath -WhatIf
# 確認
Get-ChildItem $LocateUser
Get-ChildItem $LocateMy
(Get-ChildItem $LocateCA).Where{$_.FriendlyName -like $Pattern}
(Get-ChildItem $LocateRoot).Where{$_.FriendlyName -like $Pattern}
# 2.5.29.17: Subject Alternative Name(Subject の代替名)
# 2.5.29.19: Basic Constraints(基本制約)
# 2.5.29.37: Extension(拡張機能)
# EKU 後続の文字列(OID)
# (true) CA: Certificate Authorities(認証局・証明書発行機関)
# (false) CA: エンドエンティティ(サーバー)・エンドユーザー(クライアント)証明書
# pathlength: 許可するこの証明書にぶら下がる認証局の数
# 1.3.6.1.5.5.7.3.1: Server Authentication(サーバー認証)
# 1.3.6.1.5.5.7.3.2: Client Authentication(クライアント認証)
# 証明書を構成
$RootCertParams = @{
Subject = "CN=My Root CA - UserDef"
CertStoreLocation = $LocateMy # 既定(ここに生成される)
KeyLength = 2048
HashAlgorithm = "SHA256"
KeyExportPolicy = "NonExportable" # 秘密鍵のエクスポート不可
KeyUsage = "CertSign", "CrlSign" # 他の証明書に署名する権限
TextExtension = @(
"2.5.29.19={critical}{text}CA=true&pathlength=1" # 許可する中間 CA の段階数: 1
)
NotAfter = (Get-Date).AddYears(10)
FriendlyName = "My Root CA - UserDef"
Type = "Custom"
}
$RootCert = New-SelfSignedCertificate @RootCertParams
# CA ストアからは削除
Remove-Item ("Cert:\LocalMachine\CA\" + $RootCert.Thumbprint) -WhatIf
# 自己署名した証明書を信頼(Trusted Root Certification Authorities)
$PublicRoot = [Security.Cryptography.X509Certificates.X509Certificate2]::new($RootCert.RawData)
$PublicRoot.FriendlyName = $RootCert.FriendlyName
$RootStore = [Security.Cryptography.X509Certificates.X509Store]::new("Root", "LocalMachine")
$RootStore.Open("ReadWrite")
$RootStore.Add($PublicRoot)
$RootStore.Close()
# 証明書を構成
$InterCertParams = @{
Subject = "CN=My Intermediate CA - UserDef"
CertStoreLocation = $LocateMy
Signer = $RootCert # ルート証明書から署名をもらう
KeyLength = 2048
HashAlgorithm = "SHA256"
KeyExportPolicy = "Exportable" # バックアップ用にエクスポート可能にする
KeyUsage = "CertSign", "CrlSign"
TextExtension = @(
"2.5.29.19={critical}{text}CA=true&pathlength=0", # CA をこれ以上ぶら下げない
"2.5.29.37={text}1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2" # サーバーとクライアント両方
)
NotAfter = (Get-Date).AddYears(5)
FriendlyName = "My Intermediate CA - UserDef"
Type = "Custom"
}
$InterCert = New-SelfSignedCertificate @InterCertParams
# いったん CA ストアから削除
Remove-Item ("Cert:\LocalMachine\CA\" + $InterCert.Thumbprint) -WhatIf
# 中間証明書を「中間証明機関」ストアに配置(クライアントの証明書チェーン検証用)
$PublicInter = [Security.Cryptography.X509Certificates.X509Certificate2]::new($InterCert.RawData)
$PublicInter.FriendlyName = $InterCert.FriendlyName
$InterStore = [Security.Cryptography.X509Certificates.X509Store]::new("CA", "LocalMachine")
$InterStore.Open("ReadWrite")
$InterStore.Add($PublicInter)
$InterStore.Close()
# 中間証明書へ署名したため、ルート証明書の秘密鍵は不要(検証用に公開鍵は Root ストアに残す)
$LocateMyStore = [Security.Cryptography.X509Certificates.X509Store]::new("My", "LocalMachine")
$LocateMyStore.Open("ReadWrite")
$LocateMyStore.Remove($RootCert)
$LocateMyStore.Close()
# サブジェクト代替名を設定
$SanServer = "2.5.29.17={text}" + "DNS=$HostName" + "&DNS=localhost" + "&DNS=$Env:ComputerName" + "&IPAddress=127.0.0.1"
# 証明書を構成
$ServerCertParams = @{
Subject = "CN=$HostName"
CertStoreLocation = $LocateMy
Signer = $InterCert # 中間証明書から署名をもらう
KeyLength = 2048
HashAlgorithm = "SHA256"
KeyExportPolicy = "NonExportable"
KeyUsage = "DigitalSignature", "KeyEncipherment"
TextExtension = @(
$SanServer,
"2.5.29.19={critical}{text}CA=false",
"2.5.29.37={text}1.3.6.1.5.5.7.3.1" # サーバー認証(Server Authentication)の OID を指定
)
NotAfter = (Get-Date).AddYears(1)
FriendlyName = "My Server Cert - UserDef"
Type = "Custom"
}
$ServerCert = New-SelfSignedCertificate @ServerCertParams
# サブジェクト代替名を設定
$SanClient = "2.5.29.17={text}upn=$LocalUPN"
# 証明書を構成
$ClientCertParams = @{
Subject = "CN=$UserName"
CertStoreLocation = $LocateUser # ユーザー個人の証明書ストアに直接作成
Signer = $InterCert # 中間証明書から署名をもらう
KeyLength = 2048
HashAlgorithm = "SHA256"
KeyExportPolicy = "NonExportable"
KeyUsage = "DigitalSignature"
TextExtension = @(
$SanClient,
"2.5.29.19={critical}{text}CA=false",
"2.5.29.37={text}1.3.6.1.5.5.7.3.2" # クライアント認証(Client Authentication)の OID を指定
)
NotAfter = (Get-Date).AddYears(1)
FriendlyName = "My Client Cert - UserDef"
Type = "Custom"
}
# 同時に中間証明書の公開鍵もユーザーの CA ストアにインポートされる
$ClientCert = New-SelfSignedCertificate @ClientCertParams
# 場所: Cert:\LocalMachine\Root
(Get-ChildItem Cert:\LocalMachine\Root).Where{$_.Subject -like "*UserDef"}
# 場所: Cert:\LocalMachine\CA
(Get-ChildItem Cert:\LocalMachine\CA).Where{$_.Subject -like "*UserDef"}
# 場所: Cert:\LocalMachine\My
$MyStore = [Security.Cryptography.X509Certificates.X509Store]::new("My", "LocalMachine")
$MyStore.Open("ReadWrite")
$MyStore.Certificates.Where{$_.FriendlyName -like "*UserDef"}
$MyStore.Close()
# 場所: Cert:\CurrentUser\My
$UserStore = [Security.Cryptography.X509Certificates.X509Store]::new("My", "CurrentUser")
$UserStore.Open("ReadWrite")
$UserStore.Certificates.Where{$_.FriendlyName -like "*UserDef"} | Tee-Object -Variable UserCert
$UserStore.Close()
# ユーザー証明書に対して、秘密鍵ファイルの実体からアクセス権を確認
$RsaKey = [Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($UserCert[-1])
$KeyName = $RsaKey.Key.UniqueName
$KeyPath = $Env:AppData + "\Microsoft\Crypto\Keys\" + $KeyName
Get-Acl $KeyPath | fl
備考
New-SelfSignedCertificate は証明書ストアに証明書を生成します。ファイルベースで証明書を管理したい場合は openssl 等をインストールしてください。