Managing certificates is painful. It’s kind of like brushing your teeth, if you ignore it bad things happen. Sure I’d rather deal with an expired certificate then a painful abscessed rotting tooth, but any business would not want to deal with the impact of an expired certificate on critical services. Hmm, for some reason i have lost my appetite!
Again with good house keeping, windows will alert you when certificates are about to expire, which is awesome. However the way it tells you this is far from awesome. Trawling through event logs, you might get an incredibly informative alert as follows:
1 2 3 4 5 6 7 |
Event ID: 64 Log Name: Application Event Source: CertificateServicesClient-AutoEnrollment Level: Warning Message: Certificate for local system with Thumbprint 24 6d 3d de 1e 38 06 83 aa bc cf b6 8f 29 0a 77 4a c8 13 c3 is about to expire or already expired. |
Now I don’t know about you, but I remember the hexadecimal thumbprints for all my certificates at my workplace. Surely you do as well? It’s part of the job description right?? no??. OK, let’s suppose you are some kind of savant and you know exactly what that certificate is. Well, the next statement, “it’s about to expire or ready expired”, pfftb details right?? it doesn’t make that much of a difference…. Actually, it’s a massive difference. So in summary, nice one Microsoft!
Options for checking certificate details
When it comes to checking your certificate details you have a couple of options available to you
- Do it manually with mmc and adding the certificate snapin…. Really?, sure this will work, but if you want to do that why are you reading my blog?
- Script it. Now you are talking, read on 🙂
When it comes to scripting (with Powershell of course), like most things with scripts there’s more than one way to skin a cat (I wonder where that phrase came from??, quick google.. well that’s gruesome, I wont share). Anyway, here are some scripting options
Using PSDrive to check certificates
the PSDrive “Cert” is great, you can easily browse your certificates, and get more info than just the thumbprint! (such as expiry date). Here is a quick example
1 2 3 4 5 6 7 8 9 10 11 12 13 |
PS C:\Users\Ben> cd cert: PS Cert:\> cd .\\LocalMachine\My PS Cert:\LocalMachine\My> dir | fl Subject : CN=localhost Issuer : CN=localhost Thumbprint : AAA411374BBEF65E3436C8536A3DA3AE3D1a0241 FriendlyName : IIS Express Development Certificate NotBefore : 23/03/2013 2:44:21 PM NotAfter : 23/03/2018 1:00:00 PM Extensions : {System.Security.Cryptography.Oid, System...} PS Cert:\LocalMachine\My> |
The drawback to this approach is it is harder to use remotely. If Powershell Remoting is enabled, then awesome, wrap it around an Invoke-Command ScriptBlock and Baza’s your uncle, Shaz is your aunt (that’s my Aussie take on that saying) you are done.
Use an existing script
There is an existing script in the MS scripting gallery here
https://gallery.technet.microsoft.com/scriptcenter/a2a500e5-1dd2-4898-9721-ed677399679c#content
This is good, it let’s you query a remote computer for certificate details, so ticks that box. However, you cannot specify alternate credentials. It’s funny how often this is a requirement for me!
I also found some methods using com objects that were ported from original vbscripts, I’d suggest keeping clear of that confusing mess!
My way
Right, so 500 words later, I’m actually writing something of use! The approach I’ve taken is a little different but it ticks all the boxes for me. In my research I’ve found a couple of things:
- Local Machine certificates are stored in the registry under
HKLM\SOFTWARE\Microsoft\SystemCertificates
- They are stored as a ByteArray
- This ByteArray contains the raw certificate data we can turn into an X509Certificate.
- Since it’s stored in the registry, I can specify alternate credentials as per my early blog post (the person who wrote that’s a legend).
- I need to tidy my desk
So, using what I’ve learned I put together the below script to assist with getting certificate details from a remote computer with alternate credentials. Win!, enjoy..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
function Get-PkiCertificate ($ComputerName = $env:COMPUTERNAME, $Credential) { # Int for Local Machine Registry Hive $HKLM = 2147483650 # Create our WMI registry object using alternate credentials $reg = Get-WmiObject -List -Namespace root\default -ComputerName $ComputerName -Credential $Credential | Where-Object {$_.Name -eq "StdRegProv"} # Path to certificate store - NOTE, To keep it brief, I've restricted this example to the Personal "My" store $keyPath = "SOFTWARE\Microsoft\SystemCertificates\my\Certificates" # Allow for multiple computers if passed foreach ($computer in $ComputerName) { # Enumerate all subkeys in Certificate Store Registry Path $subkeys = $reg.EnumKey($HKLM,$keyPath).sNames foreach ($key in $subkeys) { # For each certificate found get the "Blob" value and store as a Byte Array [byte[]]$byteArray = $reg.GetBinaryValue($HKLM,"$keyPath\$key","Blob").uValue # Create a new X509Certificate2 object $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 # Import byte array into newly created object, and voila! $cert.Import($byteArray) # Add ComputerName incase multiple ComputerName's submitted $cert | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $ComputerName # return the certificate object with all it's details $cert } } } |
And here is an example of it in action!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
PS C:\> Get-PkiCertificate -ComputerName web1,web2 -Credential CBF\Ben | Select ComputerName, Subject, NotAfter | fl ComputerName : web1 Subject : CN=DigiCert SHA2 Secure Server CA, O=DigiCert Inc, C=US NotAfter : 9/03/2023 1:00:00 a.m. ComputerName : web1 Subject : CN=coconuts.cloudybutfine.com NotAfter : 1/01/2018 12:00:00 a.m. ComputerName : web2 Subject : CN=*.cloudyexaample.co.nz, OU=IT, O=Clouds n sh... NotAfter : 9/05/2017 12:00:00 a.m. ComputerName : web2 Subject : CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US NotAfter : 10/11/2031 1:00:00 p.m. |