Oh boy, this has been a rollercoaster of emotions. But guys…we made it. We have finally, and definitively answered what happens to WinRM with HTTPs when certificates expire. If you’re curious about why this is a big question, see my previous posts on this topic.
Up until now, I’ve been able to say, conclusively, that WinRM generally seems to work, even as Certs expire and are renewed. But I’ve never known why: did WinRM automatically update the certs? Does Windows just not care about certs? What is the purpose of life?
Well, I can now shed light on at least some of those questions. I knew what I needed to do
Record a WireShark transfer and extract the certificate to tell definitively, which cert is being used to validate the session. Then we’ll know what happens.
Setting the stage
Two VMs, one domain. Server 2016 server, connected to from a Server 2012 R2 client. Newly created WinRM capable Certificate Template available to all domain members with a 4 hour expiration and 2 hour renewal period.
With the stage set, and the cert was present on both machines, I ran winrm quickconfig -transport:https
on each, then made sure they could see each other, and remoted from one into the other. I recorded a WireShark trace of the remote session, uh remoting, then ran a command or two, then stopped recording. Then I opened the trace.
Swimming with the Sharks

When you first open WireShark and start recording, you may be a bit dismayed…
If you were to browse a website, or do other transaction with SSL, WireShark is smart enough to break it down and show you each step in the transaction. However, with PowerShell remoting using SSL over the non-standard support of 5986
, you have to tell WireShark how to treat this data. Do this by clicking one of the first SYN \ ACK \ ECN commands, then click Analyze\ Decode as...
You’ll need to provide both the Source and Destination port (don’t worry, if you clicked one of the packets as I recommended, you can just select them from the dropdown for Value), and then pick ‘SSL’ from the dropdown list on the right.

Now you can finally see the individual steps!
Since we can see these steps, we can now drill down and see which cert is being used. That’s right, we can actually extract the certificate.
Extracting a certificate
Find the step which has the lines Server Hello, Certificate ...
and other values in it.
Now, in the Details pane below, click on Secure Sockets Layer
Follow the arrows above, and click through to TLS, Handshake Protocol: Certificate, Certificates, and finally right-click Certificate
Choose Extract Packet Bytes
and then choose where to dump the file.

With this done, you can now double-click to open the cert and see what was transmitted over the wire. Pretty crazy, huh? This is one reason why man-in-the-middle attacks are so scary. But then again, they’d have to worry about network timing, cert chains and name resolution too in order to really appear as you. But anyway, lets look and see which cert was used to authenticate this WinRM Session.

In this next screen shot, on the left is the cert I recovered from WireShark. The one on the right is the original cert from the MMC from the same computer.

So, now we’ve found out how we can recover certificates from a WireShark trace. Now all that remains is to wait the four hours for this cert to expire, and see what happens!
Waiting for the cert to renew
While I was away, I left a little chunk of code running, which will list the valid certs on the computer, and echo out their thumbprints. It also echoes out the cert listed in the HTTPS listener of WinRM. By keeping an eye on this, I know when the cert has been renewed. Here’s the code:
while ($true){ " $(get-date | select -expand DateTime) pulsing the cert store"| tee -append C:\temp\Winrm.log ; $cert = get-childitem Cert:\LocalMachine\My |? ThumbPrint -ne '9D9362043DF0027552B1B41F6F68D208F8433152' | ? ThumbPrint -ne 'FEFFA38303FA0A3748683196E350D97F869AD690' | ? ThumbPrint -ne 'A878CC677E87D5FDC852A82ECD6AFDDD6EDC3C5C'| ? ThumbPrint -ne '315E6950EB9B8DD7BCBD8263BACBDB6B35F820DF' | ? ThumbPrint -ne '232E14112D50209B2575451D63A3F7CA80AFC6EE' "--current valid thumbprint $(get-childitem Cert:\LocalMachine\My |? ThumbPrint -ne '9D9362043DF0027552B1B41F6F68D208F8433152' | ? ThumbPrint -ne 'FEFFA38303FA0A3748683196E350D97F869AD690' | ? ThumbPrint -ne 'A878CC677E87D5FDC852A82ECD6AFDDD6EDC3C5C'| ? ThumbPrint -ne '315E6950EB9B8DD7BCBD8263BACBDB6B35F820DF' | ? ThumbPrint -ne '232E14112D50209B2575451D63A3F7CA80AFC6EE' |select -ExpandProperty ThumbPrint)"| tee -append C:\temp\Winrm.log ; "--current WSman thumbprint $((get-item WSMan:\localhost\Listener\Listener_1305953032\CertificateThumbprint | select -expand Value) -replace ' ')" | tee -append C:\temp\Winrm.log ; "--cert valid $([math]::Round(($Cert.NotAfter - (get-date) | Select -expand TotalMinutes),2)) minutes, for pausing for 30 mins" start-sleep (60*30) }
So, I was really happy to see this when I came back
The difference between the current thumbprint and the one listed in WinRM told me that the cert had renewed…but strangely enough WinRM on a Server 2016 machine still references the old thumbprint.
This old thumbprint was listed EVERYWHERE. Now, the moment of truth, to run a new WireShark trace and extract the cert. I was sitting there with baited breath, very excited to see the results! And…
So…what happened?
Alright, here is what I saw when I opened the cert from the machine and saw what was listed in the MMC. It’s listed side by side with what you see in WinRM or WSMan

OK, the moment of truth. Which actual cert was used for this communication?
Did WinRM:
- A: Use the original, now expired cert
- B: Not use a cert at all?
- C: Actually use the renewed cert, even though all evidence points to the contrary?
To find out, I had to take another WireShark trace and run through all of these steps again. But what I found shocked me…
Yep. Sure enough! When decoding the certificate on the machine, I found that WinRM does actually use the renewed certificate, even though all evidence (and many sources from MSFT) point to the contrary. This is at least the case on a Server 2012 R2 machine remoting into Server 2016. Later today I’ll update with the results of 2012 to 2012, 2016 to 2016, and two goats on a chicken while a sheep watches.
What does it all mean?
In conclusion, WinRM does actually seem to handle cert expiry gracefully, at least on PowerShell 4 and up and Server 2012 R2 and newer. I’ve tested client and server connection mode from Server 2012R2 and 2016 thus far.
Credit to these fellers:
A well researched post, thanks for sharing your results Stephen!
LikeLike
This is really a great post my friend. Great work!
LikeLike
But the old cert wasn’t removed, was it? So existing connections wouldn’t sever at the time of expiration, they would continue until closed? I suspect this is the case. You’ve shown a new connection would use the new cert. I’m curious what would happen to an existing connection if the old cert was removed as part of the renewal process. Would the connection be allowed to finish? Would the connection error out? Would the cert removal fail, because it was in use?
Thank you for the thought-provoking article!
LikeLiked by 1 person
If you have a cert authority, it will allow the connection to continue. It’s a feature of active directory certificate authorities, in fact! If someone requests with a recently expired cert, that works too for (an h our) or so.
Again only if you have a certificate Authority if you’re rolling your own pki are you something different then it ends when it ends
LikeLike
To be more specific, if the cert expires and the client doesn’t get a new cert, then the connection fails as well.
WinRM uses Windows underlying PKI, and didn’t roll its own
LikeLike