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.' } } |