Public/Get-WeakCertificateReport.ps1

function Get-WeakCertificateReport {
    <#
    .SYNOPSIS
        Identifies certificates with weak or deprecated cryptography.
 
    .DESCRIPTION
        Scans the local machine certificate stores and flags certificates that
        use deprecated or weak cryptographic parameters:
 
          - SHA-1 signature algorithms
          - RSA keys shorter than the specified minimum (default 2048 bits)
          - Expired CA certificates still present in the Trusted Root store
          - Self-signed certificates in production stores (LocalMachine\My)
 
        This function helps meet compliance requirements and proactively retire
        weak certificates before they become a security incident.
 
    .PARAMETER ComputerName
        One or more computer names to scan. Accepts pipeline input.
        Defaults to localhost.
 
    .PARAMETER MinimumKeyLength
        Minimum acceptable RSA key length in bits. Certificates with shorter
        keys are flagged. Defaults to 2048.
 
    .EXAMPLE
        Get-WeakCertificateReport
 
        Scans the local machine for weak certificates using default thresholds.
 
    .EXAMPLE
        Get-WeakCertificateReport -ComputerName Server01 -MinimumKeyLength 4096
 
        Scans Server01 flagging any key shorter than 4096 bits.
 
    .OUTPUTS
        PSCustomObject with properties: Subject, Thumbprint, KeyLength,
        SignatureAlgorithm, SelfSigned, Finding.
    #>

    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string[]]$ComputerName = @('localhost'),

        [Parameter()]
        [int]$MinimumKeyLength = 2048
    )

    begin {
        Write-Verbose "Get-WeakCertificateReport - minimum key length $MinimumKeyLength bits"

        $scriptBlock = {
            param($MinimumKeyLength)

            $now = Get-Date

            # Stores to scan: Personal, Root, CA
            $stores = @(
                @{ Location = 'LocalMachine'; Name = 'My'   }
                @{ Location = 'LocalMachine'; Name = 'Root' }
                @{ Location = 'LocalMachine'; Name = 'CA'   }
            )

            foreach ($store in $stores) {
                $storePath = "Cert:\$($store.Location)\$($store.Name)"
                if (-not (Test-Path $storePath)) { continue }

                Get-ChildItem -Path $storePath -ErrorAction SilentlyContinue | ForEach-Object {
                    $cert     = $_
                    $findings = [System.Collections.Generic.List[string]]::new()

                    # --- Key length ---------------------------------------------------
                    $keyLength = 0
                    try {
                        if ($cert.PublicKey.Key) {
                            $keyLength = $cert.PublicKey.Key.KeySize
                        }
                        elseif ($cert.PublicKey.EncodedKeyValue) {
                            $keyLength = $cert.PublicKey.EncodedKeyValue.RawData.Length * 8
                        }
                    }
                    catch { }

                    if ($keyLength -gt 0 -and $keyLength -lt $MinimumKeyLength) {
                        $findings.Add("WEAK_KEY (${keyLength}-bit < ${MinimumKeyLength}-bit)")
                    }

                    # --- Signature algorithm ------------------------------------------
                    $sigAlg = $cert.SignatureAlgorithm.FriendlyName
                    if ($sigAlg -match 'sha1' -and $sigAlg -notmatch 'sha1RSA.*obsolete') {
                        $findings.Add('SHA1_SIGNATURE')
                    }

                    # --- Self-signed in production store ------------------------------
                    $selfSigned = $cert.Subject -eq $cert.Issuer
                    if ($selfSigned -and $store.Name -eq 'My') {
                        $findings.Add('SELF_SIGNED_IN_MY')
                    }

                    # --- Expired CA in Root store -------------------------------------
                    if ($store.Name -eq 'Root' -and $cert.NotAfter -lt $now) {
                        $findings.Add('EXPIRED_ROOT_CA')
                    }

                    # Only return certificates that have at least one finding
                    if ($findings.Count -gt 0) {
                        [PSCustomObject]@{
                            Subject            = $cert.Subject
                            Thumbprint         = $cert.Thumbprint
                            KeyLength          = $keyLength
                            SignatureAlgorithm = $sigAlg
                            SelfSigned         = $selfSigned
                            Store              = "$($store.Location)\$($store.Name)"
                            ComputerName       = $env:COMPUTERNAME
                            Finding            = $findings -join '; '
                        }
                    }
                }
            }
        }
    }

    process {
        foreach ($computer in $ComputerName) {
            Write-Verbose "Scanning $computer for weak certificates"

            try {
                $isLocal = $computer -eq 'localhost' -or
                           $computer -eq $env:COMPUTERNAME -or
                           $computer -eq '.'

                if ($isLocal) {
                    $results = & $scriptBlock -MinimumKeyLength $MinimumKeyLength
                }
                else {
                    $results = Invoke-Command -ComputerName $computer `
                                              -ScriptBlock $scriptBlock `
                                              -ArgumentList $MinimumKeyLength `
                                              -ErrorAction Stop
                }

                if ($results) {
                    $results | ForEach-Object { $_ }
                }
                else {
                    Write-Verbose "No weak certificates found on $computer."
                }
            }
            catch {
                Write-Warning "Failed to scan ${computer}: $_"
            }
        }
    }

    end {
        Write-Verbose 'Get-WeakCertificateReport complete.'
    }
}