Private/Collectors/Get-RemoteCertificate.ps1
|
function Get-RemoteCertificate { [CmdletBinding()] [OutputType([object[]])] param( [Parameter(Mandatory)][System.Management.Automation.Runspaces.PSSession]$Session, [bool]$IncludeAllStores = $false ) Invoke-Command -Session $Session -ArgumentList $IncludeAllStores -ScriptBlock { param($includeAll) $defaultStores = 'My','WebHosting','TrustedPublisher','Root','CA' $allStores = (Get-ChildItem Cert:\LocalMachine -ErrorAction SilentlyContinue).PSChildName $stores = if ($includeAll) { $allStores } else { $defaultStores } $now = Get-Date foreach ($store in $stores) { $path = "Cert:\LocalMachine\$store" if (-not (Test-Path $path)) { continue } Get-ChildItem $path -ErrorAction SilentlyContinue | ForEach-Object { $cert = $_ $san = @() $eku = @() foreach ($ext in $cert.Extensions) { switch ($ext.Oid.Value) { '2.5.29.17' { # SAN extraction. X509SubjectAlternativeNameExtension is .NET 5+ only; # Windows PowerShell 5.1 (.NET Framework 4.x) on the remote target lacks it. # Use the modern type if available, otherwise parse Format() output. try { $sanType = [System.Security.Cryptography.X509Certificates.X509SubjectAlternativeNameExtension] -as [type] if ($sanType) { $sanExt = $sanType::new() $sanExt.CopyFrom($ext) $san = @($sanExt.EnumerateDnsNames()) } else { # Locale-tolerant: any "...DNS...=value" line. Handles English # ("DNS Name=foo"), German ("DNS-Name=foo"), and other variants. $san = @($ext.Format($true) -split "[\r\n]+" | ForEach-Object { $line = $_.Trim() if ($line -match '^(?:[^=]*DNS[^=]*)=(.+)$') { $matches[1].Trim() } } | Where-Object { $_ }) } } catch { $san = @() } } '2.5.29.37' { try { $eku = @(([System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension]$ext).EnhancedKeyUsages | ForEach-Object FriendlyName) } catch { $eku = @() } } } } [pscustomobject]@{ Store = "LocalMachine\$store" Subject = $cert.Subject Issuer = $cert.Issuer Thumbprint = $cert.Thumbprint SerialNumber = $cert.SerialNumber NotBeforeUtc = $cert.NotBefore.ToUniversalTime().ToString('o') NotAfterUtc = $cert.NotAfter.ToUniversalTime().ToString('o') DaysUntilExpiry = [int]([math]::Floor(($cert.NotAfter - $now).TotalDays)) Expired = ($cert.NotAfter -lt $now) HasPrivateKey = $cert.HasPrivateKey FriendlyName = $cert.FriendlyName SubjectAlternativeNames = @($san) EnhancedKeyUsage = @($eku) } } } } | Select-Object Store, Subject, Issuer, Thumbprint, SerialNumber, NotBeforeUtc, NotAfterUtc, DaysUntilExpiry, Expired, HasPrivateKey, FriendlyName, SubjectAlternativeNames, EnhancedKeyUsage } |