Important Notice: On February 29th, this community was put into read-only mode. All existing posts will remain but customers are unable to add new posts or comment on existing. Please feel to join our Community Discord for any questions and discussions.

SSL Certificate Scanning

Has anyone used PDQ Inventory to scan installed SSL certificates?  It would be helpful in keeping track of where SSL certificates are used so they're not missed when the cert is renewed.

0

Comments

10 comments
Date Votes
  • Mike - I just spent some time figuring it out better. I use Powershell to grab the cert info (subject, issuer, issue date and expiry date) and then I use Dynamic Groups to organize my certs into public and private and then I have automated reports that let me know what certs are coming up for renewal (either public or private) - so I can stay on top of them.

    0
  • asolomon

    Can you share the PS you're running please?

    0
  • Get-ChildItem Cert:\ -Recurse | Where-Object {$_.Subject -Match '{yourdomain}.com'}|Select-Object Subject, Issuer, NotBefore, NotAfter

    Replace {yourdomain}.com with your domain.

    I use this in a Scheduled Scan that pulls the certs from all my servers and stores them. Then I have a dynamic collection based on all certs where the "Issuer" contains the name (or names) of the companies that provide my public certs - so like contains "Sectigo" or "zerossl". This will put all of my servers in a single collection that have public certs. Then I have a scheduled report that runs and emails to my admins a list of all the above info for all servers where the certs NotAfter < 365 days - so that my admins know what certs are coming due in the year and when.

    Let me know if that makes sense to you.

    0
  • asolomon

    That worked perfectly!  Thank you so much for sharing!

    0
  • asolomon

    Could you post a screenshot of your dynamic collection?  I'm having an issue with the Column/Comparison/Value.

    0
  • I replaced items names with other things so you could see an example. You only need the second section with PowerShell... The reason for the first section is because I have some non-windows machines that also have certificates installed, like firewalls, and I track them in PDQ (but can't scan them), so I include the 4 objects that have certs, so that when I look at the dynamic collection or run a report, I get those 4 machines in the report and don't forget to update them. The last line Computer O/S Name is a variable I use to include all the parameters of what defines a "Server" in my environment, instead of using the built in options - so this will give me a list of all "Servers" that have a cert where the issuer is "Sectigo" so I know what PUBLIC certs I need to worry about.

    I have another collection where it looks at the Issuer as my company name/PKI info so I can see what internal certs I have issued that also will have expiration dates - I just don't need to care as much as I do about the public certs that expire every year.

    Then I run a bi-monthly report to my admins showing them what's coming up and when so they can plan accordingly. Let me know if that makes sense to you.

    0
  • Makes perfect sense...thank you again!

    I was trying to build a dynamic collection based on the NotAfter column:

    So I could know which machines have or have not been updated.

    0
  • I do the calculation in the report. I want all servers that have public certs in one collection. I don't want to miss anything. Then, my report looks at the NotAfter but I use 365 days instead of a date so it's always showing relative data. I BOTHER my admins 6 times a year to look at it and make sure they don't miss it and know what's coming up and they have a couple of heads up emails before there's a problem. Accountability is now there and they can plan accordingly. I have a bunch of Certs and they expire throughout the year, so I need them to see them coming up and be reminded. For the internal certs, it looks ahead 730 days, but that was just my personal preference. The problem with using a fixed date is you have to keep updating it. You could set to look only so far in advance, but I prefer to do that in the report than in the dynamic collection - also because a report is pushed and then email archived -  again, accountability. I like push notifications for timely things. I also cc myself on the report to make sure it's handled, as a backup.

    0
  • Mike, You could also do this....

    That will show you what's expiring in the next 60 days. You can also use a fixed date, if you like.

    0
  •  

    Hello,

    I know this is a bit of a wild question, but how would one use this for ADFS Certificates? Cause they are not stored in the regular Local Machine or Current User.

     

    You can use the PS command  to get it manually, but how would I be able to rewrite the script from below to be able to also account for our ADFS Server?

    Get-AdfsCertificate –CertificateType token-signing

    Get-AdfsCertificate –CertificateType token-decrypting
    [CmdletBinding()]
    param (
        [ValidateSet('Archived', 'DnsNameList', 'EnhancedKeyUsageList', 'EnrollmentPolicyEndPoint',
            'EnrollmentServerEndPoint', 'Extensions', 'FriendlyName', 'Handle', 'HasPrivateKey', 'Issuer', 'IssuerName',
            'NotAfter', 'NotBefore', 'PolicyId', 'PrivateKey', 'PSChildName', 'PSDrive', 'PSIsContainer', 'PSParentPath',
            'PSPath', 'PSProvider', 'PublicKey', 'RawData', 'SendAsTrustedIssuer', 'SerialNumber', 'SignatureAlgorithm',
            'Subject', 'SubjectName', 'Thumbprint', 'Version', '*')]
        [String[]]$Property = [String[]]('FriendlyName', 'NotBefore', 'NotAfter', 'IssuerName', 'Issuer', 'Thumbprint', 'SerialNumber', 'Subject', 'PSParentPath'),

        [ValidateSet('CurrentUser', 'LocalMachine', 'AD FS')]
        [String[]]$StoreLocation = @('CurrentUser', 'LocalMachine', 'AD FS'),

        [String[]]$StoreName
    )

    $CertType = [System.Security.Cryptography.X509Certificates.X509Certificate2]

    # Replace PSPath and PSParentPath with expressions that will trim their output.
    $Properties = @()
    foreach ( $PropertyIterator in $Property ) {

        if ( $PropertyIterator -eq 'PSPath' ) {

            $Properties += @{Label = 'PSPath'; Expression = { ($_.PSPath -split ':')[-1] } }

        } elseif ($PropertyIterator -eq 'PSParentPath') {
            
            $Properties += @{Label = 'PSParentPath'; Expression = { ($_.PSParentPath -split ':')[-1] } }
        
        } else {

            $Properties += $PropertyIterator

        } else

    }

    foreach ( $StoreLocationIterator in $StoreLocation ) {

        if ( $StoreName ) {

            foreach ( $StoreNameIterator in $StoreName ) {

                $Param = @{
                    'Path'        = "Cert:\$StoreLocationIterator\$StoreNameIterator"
                    'ErrorAction' = 'SilentlyContinue'
                }
                Get-ChildItem @Param | Where-Object { $_ -is $CertType } | Select-Object $Properties

            }

        } else {

            $Param = @{
                'Path'        = "Cert:\$StoreLocationIterator"
                'ErrorAction' = 'SilentlyContinue'
                'Recurse'     = $true
            }
            Get-ChildItem @Param | Where-Object { $_ -is $CertType } | Select-Object $Properties

        }

    }
    0