Private/Collectors.Certificates.ps1

function Get-ICCertificateEnhancedKeyUsage {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate
    )

    $usages = New-Object System.Collections.ArrayList
    foreach ($extension in @($Certificate.Extensions)) {
        if ($extension -isnot [System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension]) {
            continue
        }
        foreach ($oid in @($extension.EnhancedKeyUsages)) {
            $label = if (-not [string]::IsNullOrWhiteSpace($oid.FriendlyName)) { $oid.FriendlyName } else { $oid.Value }
            if (-not [string]::IsNullOrWhiteSpace($label)) {
                [void]$usages.Add($label)
            }
        }
    }
    return ($usages -join '; ')
}

function Get-ICCertificateEvidence {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [object]$Context
    )

    $warnings = New-Object System.Collections.ArrayList
    $files = New-Object System.Collections.ArrayList

    $stores = @('Root', 'CA', 'AuthRoot', 'TrustedPublisher', 'TrustedPeople', 'Disallowed')
    $entries = New-Object System.Collections.ArrayList
    $storesQueried = 0
    foreach ($storeName in $stores) {
        $storePath = "Cert:\LocalMachine\$storeName"
        if (-not (Test-Path -LiteralPath $storePath)) {
            Add-ICCollectorWarning -List $warnings -Message "Certificate store '$storeName' is unavailable."
            continue
        }
        try {
            $storesQueried++
            foreach ($certificate in Get-ChildItem -LiteralPath $storePath -ErrorAction Stop) {
                if ($certificate -isnot [System.Security.Cryptography.X509Certificates.X509Certificate2]) {
                    continue
                }
                $signatureAlgorithm = $null
                try { $signatureAlgorithm = $certificate.SignatureAlgorithm.FriendlyName } catch { $signatureAlgorithm = $null }
                $keyAlgorithm = $null
                try { $keyAlgorithm = $certificate.PublicKey.Oid.FriendlyName } catch { $keyAlgorithm = $null }

                [void]$entries.Add([pscustomobject][ordered]@{
                    Store              = $storeName
                    Subject            = [string]$certificate.Subject
                    Issuer             = [string]$certificate.Issuer
                    Thumbprint         = [string]$certificate.Thumbprint
                    SerialNumber       = [string]$certificate.SerialNumber
                    NotBeforeUtc       = ConvertTo-ICIso8601 -Value $certificate.NotBefore
                    NotAfterUtc        = ConvertTo-ICIso8601 -Value $certificate.NotAfter
                    SignatureAlgorithm = $signatureAlgorithm
                    KeyAlgorithm       = $keyAlgorithm
                    EnhancedKeyUsages  = Get-ICCertificateEnhancedKeyUsage -Certificate $certificate
                    IsSelfSigned       = ($certificate.Subject -eq $certificate.Issuer)
                    HasPrivateKey      = [bool]$certificate.HasPrivateKey
                    Archived           = [bool]$certificate.Archived
                })
            }
        }
        catch { Add-ICCollectorWarning -List $warnings -Message "Certificate store '$storeName': $($_.Exception.Message)" }
    }

    $sorted = @($entries | Sort-Object Store, Subject, Thumbprint)
    Add-ICOutputFiles -List $files -Path (Export-ICCollectorData -Context $Context -Collector Certificates -RelativePath 'evidence/certificates/certificate-stores.json' -Data $sorted -Csv)

    return New-ICCollectorResultData -OutputFiles @($files) -Warnings @($warnings) -Metrics ([ordered]@{
        CertificateCount       = $sorted.Count
        StoresQueried          = $storesQueried
        NonSelfSignedRootEntries = @($sorted | Where-Object { $_.Store -eq 'Root' -and $_.IsSelfSigned -eq $false }).Count
        DisallowedEntries      = @($sorted | Where-Object Store -eq 'Disallowed').Count
    })
}