Forum Discussion

jhipes's avatar
2 years ago

Pulling SSL certs from a local Certificate store

I am wondering if any one has been able to pull SSL certificate info from a servers local computer certificate store. I can get on the server and run the below PowerShell command and it pulls all of ...
  • mnagel's avatar
    2 years ago

    I created a module for this some time back -- should be in the Exchange.  I just looked at ours, though, and it is marked ‘Security Review’ which is super annoying since it was submitted 2 years ago at least -- LM has no apparent workflow for the Exchange, just have to find the right person to poke (if you get lucky). There should at least be some sort of “escalate” button on those to get someone’s attention.

    I also don’t think the version posted is our current version (we added some code later to avoid expired certs in some cases).  Here is the full source code (AD and collection).  Somewhat ironically, the code handler for the forum post editor does not include “PowerShell”.

    $TargetHostName = '##SYSTEM.SYSNAME##'
    $CollectorHostName = $env:computername
    $TargetUserName = '##wmi.user##'
    $TargetPassword = '##wmi.pass##'
    $DebugMode = $false


    $ScriptBlock = {
    param (
    [bool]$Debug
    )

    # Certificates need to have a lifespan of at least this number of days to be considered.
    $MinLifeSpan = 30

    # Certificates that are expired by more than this many days are ignored.
    $MaxExpired = 60

    $AllCerts = @()
    $CertExpire = @{}


    $CertList = Get-ChildItem -Path cert:LocalMachine -Recurse -SSLServerAuthentication
    Foreach ($Cert in $CertList) {
    $SerialNumber = $Cert.SerialNumber
    $FriendlyName = $Cert.FriendlyName;
    $Name = ""
    if ($FriendlyName -eq "")
    {
    $Name = $SerialNumber
    }
    else
    {
    $Name = "$FriendlyName - $SerialNumber"
    }

    $NotBefore = $Cert.NotBefore
    $NotAfter = $Cert.NotAfter
    $LifeSpan = New-TimeSpan -Start $NotBefore -End $NotAfter
    $LifeSpanDays = $LifeSpan.Days

    $Expired = New-TimeSpan -Start $NotAfter -End (Get-Date)
    $ExpiredDays = $Expired.Days

    if (($SerialNumber -ne "") -AND ($LifeSpanDays -gt $MinLifeSpan) -AND ($ExpiredDays -lt $MaxExpired)) {
    $Props = @()
    $propPrefix = "auto.windowscerts"
    $Props += "${propPrefix}.serialnum=$SerialNumber"
    $Store = ($Cert.PSParentPath -split "::")[1]
    #
    # Add this store to the cert expiration tracking hashtable if it isn't
    # there already
    #
    if ( -not $CertExpire.ContainsKey($Store) ){
    $CertExpire[$Store] = @{}
    }

    $Props += "${propPrefix}.lifespan_days=$LifeSpanDays"

    if ($Store -ne "") { $Props += "${propPrefix}.store=$Store" }
    if ($NotBefore -ne "") { $Props += "${propPrefix}.starts=$NotBefore" }
    if ($NotAfter -ne "") { $Props += "${propPrefix}.expires=$NotAfter" }
    $Subject = $Cert.Subject
    if ($Subject -ne "") { $Props += "${propPrefix}.subject=$Subject" }
    $Issuer = $Cert.Issuer
    if ($Issuer -ne "") { $Props += "${propPrefix}.issuer=$Issuer" }
    $Thumbprint = $Cert.Thumbprint
    if ($Thumbprint -ne "") { $Props += "${propPrefix}.thumbprint=$Thumbprint" }
    $DNSNameList = $Cert.DNSNameList
    if ($DNSNameList -ne "") { $Props += "${propPrefix}.dnsnames=$DNSNameList" }

    # If $ExpiredDays is negative it means the cert has not expired yet.
    # We care about all non-expired certs but only the most recent of expired certs with
    # the same CN in the same store
    $AllCerts += @{
    Subject = $Subject
    Store = $Store
    Name = $Name
    SerialNumber = $SerialNumber
    ExpiredDays = $ExpiredDays
    Props = $Props
    }

    if ( $CertExpire[$Store].ContainsKey($Subject) ){
    if ( $ExpiredDays -lt $CertExpire[$Store][$Subject] ){
    $CertExpire[$Store][$Subject] = $ExpiredDays
    }
    }
    else {
    $CertExpire[$Store][$Subject] = $ExpiredDays
    }

    # Write-Host "$SerialNumber##$Name##$Subject####", ($Props -join '&')
    }
    }
    foreach ( $Cert in $AllCerts ){
    $Subject = $Cert.Subject
    $Store = $Cert.Store
    $ExpiredDays = $Cert.ExpiredDays


    if ( ($ExpiredDays -gt 0) -AND ($ExpiredDays -gt $CertExpire[$Store][$Subject]) ){
    # Don't care about expired certs when a newer one exists in the same store
    # with the same Subject
    continue
    }

    $SerialNumber = $Cert.SerialNumber
    $Name = $Cert.Name
    $Props = $Cert.Props

    Write-Host "$SerialNumber##$Name##$Subject####", ($Props -join '&')
    }
    }

    if ($TargetHostName -eq $CollectorHostName)
    {
    Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList @($DebugMode)
    }
    else
    {
    #If WMI creds undefined - invoke remote command without creds, if not - create credential object and use it for authorization
    if (($TargetUserName -Match "wmi.user" -and $TargetPassword -Match "wmi.pass") -or
    ($TargetUserName -eq "" -and $TargetPassword -eq ""))
    {
    Invoke-Command -ComputerName $TargetHostName -ScriptBlock $ScriptBlock -ArgumentList @($DebugMode)
    }
    else
    {
    $TargetCredentials = New-Object System.Management.Automation.PSCredential ($TargetUserName, $(ConvertTo-SecureString $TargetPassword -AsPlainText -Force))
    Invoke-Command -ComputerName $TargetHostName -ScriptBlock $ScriptBlock -Credential $TargetCredentials -ArgumentList @($DebugMode)
    }
    }

    Exit 0
    $Instance = '##WILDVALUE##'
    $TargetHostName = '##SYSTEM.SYSNAME##'
    $CollectorHostName = $env:computername
    $TargetUserName = '##wmi.user##'
    $TargetPassword = '##wmi.pass##'
    $DebugMode = $false

    $ScriptBlock = {
    param (
    [bool]$Debug,
    [String]$SerialNumber
    )

    if ($Cert = Get-ChildItem -Path cert:LocalMachine -Recurse | Where-Object { $_.SerialNumber -eq $SerialNumber }) {
    # NotAfter can return multiple values, we'll just take the first (zeroth) one using [0]
    $TimeSpan = New-TimeSpan -Start (Get-Date) -End $Cert.NotAfter[0]
    $DaysLeft = $TimeSpan.Days
    Write-Host "DaysLeft=$DaysLeft"
    }
    }

    if ($TargetHostName -eq $CollectorHostName)
    {
    Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList @($DebugMode,$Instance)
    }
    else
    {
    #If WMI creds undefined - invoke remote command without creds, if not - create credential object and use it for authorization
    if (($TargetUserName -Match "wmi.user" -and $TargetPassword -Match "wmi.pass") -or
    ($TargetUserName -eq "" -and $TargetPassword -eq ""))
    {
    Invoke-Command -ComputerName $TargetHostName -ScriptBlock $ScriptBlock -ArgumentList @($DebugMode,$Instance)
    }
    else
    {
    $TargetCredentials = New-Object System.Management.Automation.PSCredential ($TargetUserName, $(ConvertTo-SecureString $TargetPassword -AsPlainText -Force))
    Invoke-Command -ComputerName $TargetHostName -ScriptBlock $ScriptBlock -Credential $TargetCredentials -ArgumentList @($DebugMode,$Instance)
    }
    }

    Exit 0