この記事について
この記事は、生成AIを活用した自動生成フローで作成しています。.NETのTcpClient・SslStream・X509Certificate2公式資料を確認し、TLS接続後にサーバー証明書の有効期限を読み取る最小例へ整理しています。検証ステータス:📘 .NET公式仕様確認済み・外部TLS接続は未実行
ブラウザ画面を開かなくても、PowerShellからTLS接続し、相手が提示した証明書のSubjectと有効期限を確認できます。証明書検証を無効化せず、SslStream の通常の認証フローを使うのがポイントです。
まず試す
$hostName = 'www.microsoft.com'
$tcp = [Net.Sockets.TcpClient]::new()
$ssl = $null
try {
$tcp.Connect($hostName, 443)
$ssl = [Net.Security.SslStream]::new($tcp.GetStream(), $false)
$ssl.AuthenticateAsClient($hostName)
$cert = [Security.Cryptography.X509Certificates.X509Certificate2]::new(
$ssl.RemoteCertificate
)
[pscustomobject]@{
Host = $hostName
Subject = $cert.Subject
NotAfter = $cert.NotAfter
DaysLeft = [math]::Floor(($cert.NotAfter - (Get-Date)).TotalDays)
}
}
finally {
if ($null -ne $ssl) { $ssl.Dispose() }
$tcp.Dispose()
}
ここを見る
AuthenticateAsClient() が完了した後に RemoteCertificate を読みます。DaysLeft は「今からNotAfterまで何日あるか」の観察値です。
なぜhost名を渡すのか
TLSでは、接続先IPだけでなく「どのhost名へ接続するか」が重要です。AuthenticateAsClient($hostName) のhost名は、サーバー名を使ったTLS認証に関係します。
sequenceDiagram
participant P as PowerShell
participant T as TcpClient
participant S as TLS Server
P->>T: host:443へ接続
T->>S: TCP接続
P->>S: AuthenticateAsClient(host)
S-->>P: 証明書を提示
P->>P: RemoteCertificateを読む
1か所変えてみる
www.microsoft.com を github.com に変え、SubjectやNotAfterがどう変わるか比較します。
$hostName = 'github.com'
同じコードでも相手が変われば証明書情報が変わります。これで「443番ポートの期限を見る」のではなく、接続先が提示した証明書を見ることが分かります。
期限だけで「正常」と判断しない
NotAfterが未来でも、それだけでTLSが健全とは断定できません。名前の不一致、信頼chain、失効、TLS設定など別の要素があります。
またCDNやload balancerを使うサイトでは、実際に手前のサービスが提示する証明書を観察している場合があります。内部originの証明書を見ているとは限りません。
仕事で使うなら
社内Webサイトの証明書更新忘れを事前確認する
複数FQDNの残日数を一覧化する前の最小検証に使う
「ブラウザで見える証明書」と自動監視の結果を突き合わせる
ベンダーへ問い合わせる前に、実際に提示されたSubject/NotAfterを記録する
監視へ発展させる場合は、1回失敗しただけで期限切れと判定せず、DNS・TCP接続・TLS認証・証明書取得のどこで失敗したかを分けて記録すると実務で使いやすくなります。
後始末が必要な理由
TcpClient と SslStream は接続資源を持つため、finally でDisposeします。特に複数hostを巡回する処理へ拡張する場合、後始末を省くと接続資源を不要に残す原因になります。
