An unexpected expiration of a server certificate can cause a number of problems for your users and customers: they may not be able to establish a secure connection with your site, authentication errors may occur, annoying notifications may appear in a browser, etc. In this article we’ll show how to check the expiration date of an SSL/TLS certificate on remote sites, or get a list of expiring certificates in the local certificate store on servers or computers in your domain.
Get the Expiration Date of a Website SSL Certificate with PowerShell
Many web projects use free Let’s Encrypt SSL certificates to implement HTTPS. These certificates are issues for 90 days and must be renewed regularly. Usually, special scripts or bots update Let’s Encrypt certificates on the hosting or server side (it may be WACS in Windows or Certbot in Linux). However, sometimes automatic certificate renewal fails. I would like to have my own script that would check SSL certificate expiry dates on websites and notify me when they are about to expire. I used PowerShell to create it. Since we are checking a website’s certificate via an HttpWeb query, we don’t need administrator privileges on a remote website/server.
In the following PowerShell script, you must specify the list of website you want to check certificate expiration dates on and the certificate age when the corresponding notification starts to be displayed to you ($minCertAge
). I entered 80 days as an example.
$minCertAge = 80
$timeoutMs = 10000
$sites = @(
"https://testsite1.com/",
"https://testsite2.com/",
"https://woshub.com/"
)
# Disable certificate validation
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
foreach ($site in $sites)
{
Write-Host Check $site -f Green
$req = [Net.HttpWebRequest]::Create($site)
$req.Timeout = $timeoutMs
try {$req.GetResponse() |Out-Null} catch {Write-Host URL check error $site`: $_ -f Red}
$expDate = $req.ServicePoint.Certificate.GetExpirationDateString()
$certExpDate = [datetime]::ParseExact($expDate, “dd/MM/yyyy HH:mm:ss”, $null)
[int]$certExpiresIn = ($certExpDate - $(get-date)).Days
$certName = $req.ServicePoint.Certificate.GetName()
$certThumbprint = $req.ServicePoint.Certificate.GetCertHashString()
$certEffectiveDate = $req.ServicePoint.Certificate.GetEffectiveDateString()
$certIssuer = $req.ServicePoint.Certificate.GetIssuerName()
if ($certExpiresIn -gt $minCertAge)
{Write-Host The $site certificate expires in $certExpiresIn days [$certExpDate] -f Green}
else
{
$message= "The $site certificate expires in $certExpiresIn days"
$messagetitle= "Renew certificate"
Write-Host $message [$certExpDate]. Details:`n`nCert name: $certName`Cert thumbprint: $certThumbprint`nCert effective date: $certEffectiveDate`nCert issuer: $certIssuer -f Red
#Displays a pop-up notification and sends an email to the administrator
#ShowNotification $messagetitle $message
# Send-MailMessage -From [email protected] -To [email protected] -Subject $messagetitle -body $message -SmtpServer gwsmtp.woshub.com -Encoding UTF8
}
write-host "________________" `n
}
This PowerShell script will check SSL certificates of all websites in the list. If a certificate is found that is about to expire, it will be highlighted in the notification.
To notify an administrator that an SSL certificate is about to expire, you can add a popup notification. To do it, uncomment the script line “ShowNotification $messagetitle $message
” and add the following function:
Function ShowNotification ($MsgTitle, $MsgText) {
Add-Type -AssemblyName System.Windows.Forms
$global:balmsg = New-Object System.Windows.Forms.NotifyIcon
$path = (Get-Process -id $pid).Path
$balmsg.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path)
$balmsg.BalloonTipIcon = [System.Windows.Forms.ToolTipIcon]::Warning
$balmsg.BalloonTipText = $MsgText
$balmsg.BalloonTipTitle = $MsgTitle
$balmsg.Visible = $true
$balmsg.ShowBalloonTip(10000)
}
You can also send an email notification using Send-MailMessage
.
Then if any expired or expiring certificates are found, you will be notified by an email and a popup message.
Then create an automatic task for the Task Scheduler to be run once or twice a week and run the PowerShell script to check expiry dates of your HTTPS website certificates. (You can create a task in the Task Scheduler to run a PS1 script file using Register-ScheduledTask cmdlet.)
How to Check for Expired Certificates in Windows Certificate Store Remotely?
You may also need a PowerShell script check the expiration dates of certificates used by cryptographic services on your domain servers (e. g., RDP/RDS , Exchange, SharePoint, LDAPS certificates, etc.) or users’ computers.
On a local computer, you can get a list of certificates using the command:
Get-ChildItem -Path cert
Powershell 3.0 has a special -ExpiringInDays
argument:
Get-ChildItem -Path cert: -Recurse -ExpiringInDays 30
In PowerShell 2.0, the same command looks like this:
Get-ChildItem -Path cert: -Recurse | where { $_.notafter -le (get-date).AddDays(30) -AND $_.notafter -gt (get-date)} | select thumbprint, subject
To check only your own certificates, use the Cert:\LocalMachine\My
container instead of Cert:
in the root folder. Thus, you won’t check Windows trusted root certificates and commercial certificates.
To find certificates that will expire in the next 30 days on all domain servers, use this PowerShell script:
$servers= (Get-ADComputer -LDAPFilter "(&(objectCategory=computer)(operatingSystem=Windows Server*) (!serviceprincipalname=*MSClusterVirtualServer*) (!(userAccountControl:1.2.840.113556.1.4.803:=2)))").Name
$result=@()
foreach ($server in $servers)
{
$ErrorActionPreference="SilentlyContinue"
$getcert=Invoke-Command -ComputerName $server { Get-ChildItem -Path Cert:\LocalMachine\My -Recurse -ExpiringInDays 30}
foreach ($cert in $getcert) {
$result+=New-Object -TypeName PSObject -Property ([ordered]@{
'Server'=$server;
'Certificate'=$cert.Issuer;
'Expires'=$cert.NotAfter
})
}
}
Write-Output $result
You will get the list of server certificates that are about to expire and you will have enough time to renew them.
7 comments
line: $certExpDate = [datetime]::ParseExact($expDate, “dd/MM/yyyy HH:mm:ss”, $null):
error: Exception calling “ParseExact” with “3” argument(s): “String was not recognized as a valid DateTime.”
locate: zh-CN,china
Check _https://v16mdm.*****.com:8443/
使用“3”个参数调用“ParseExact”时发生异常:“该字符串未被识别为有效的 DateTime。”
所在位置 D:\crt.ps1:17 字符: 1
+ $certExpDate = [datetime]::ParseExact($expDate, “yyyy/MM/dd HH:mm:ss” …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : FormatException
The _https://v16mdm.*****.com:8443/ certificate expires in -737723 days []. Details:
Cert name: CN=v16mdm.*****.comCert thumbprint: 8A13A833979173E992E51602B41BC165097E8D71
Cert effective date: 2019/11/5 8:00:00
Cert issuer: C=CN, O=”TrustAsia Technologies, Inc.”, OU=Domain Validated SSL, CN=TrustAsia TLS RSA CA
________________
Check _https://jumpserver.*****.com/
The _https://jumpserver.*****.com/ certificate expires in 26 days [11/22/2020 13:29:54]. Details:
Cert name: CN=jumpserver.*****.comCert thumbprint: 8E5E3AE79075E12C3D6B721203850C6821F65019
Cert effective date: 2020/8/24 13:29:54
Cert issuer: C=US, O=Let’s Encrypt, CN=Let’s Encrypt Authority X3
It looks like your computer is using the local date/time format. You need to change the date format on your Windows computer or convert the $expDate variable to your datetime format.
i install en-us lanauge win 2019 test the issue is also;
the Let’s Encrypt Authority X3 check is ok, Is it related to cert or need Processing datetime format code;
Sorry for my bad english, tks
tks to try:
“https://www.solves.com.cn/”,
“https://freessl.cn/”
$certName = $req.ServicePoint.Certificate.GetName()
BindIPEndPointDelegate :
ConnectionLeaseTimeout : -1
Address : https://www.outlook.com/
MaxIdleTime : 100000
UseNagleAlgorithm : True
ReceiveBufferSize : -1
Expect100Continue : True
IdleSince : 12/30/2020 1:30:41 PM
ProtocolVersion : 1.1
ConnectionName : https
ConnectionLimit : 2
CurrentConnections : 0
Certificate :
ClientCertificate :
SupportsPipelining : True
i don’t see any value in certificate row and its failing with “You cannot call a method on a null-valued expression” error
I also got invalid date for $expDate so I had to clean it up to remove the “AM” that was being appended. AM or PM doesn’t matter, I can loose 12 hours and not know the difference. Know what i mean? 😉
So i added this line above the ParseExact line:
$expDate = get-date $expDate -Format “MM/dd/yyyy HH:mm:ss”
Hi All,
I have created a new version
Create DNS.txt file, the file will contain the following
abc.com
xyz.com
yyy.com
Create new PowerShell file SSL.ps1, copy paste following, test it out
cls
$listOfSites = @()
$minCertAge = 30
$timeoutMs = 30000
$sites = $null
$messagetitle= "Website SSL Certificate Status"
$sites = Get-Content "E:\Portal\Scripts\VerifyCertificate\DNS.txt"
Add-Type -AssemblyName System.Web
$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
[System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols
# Disable certificate validation
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
foreach ($site in $sites)
{
$site = "https://" + $site
Write-Host Check $site -f Green
$req = [Net.HttpWebRequest]::Create($site)
$req.Timeout = $timeoutMs
try {
$req.GetResponse() |Out-Null
}
catch
{
Write-Host URL check error $site`: $_ -f Red
}
$expDate = $req.ServicePoint.Certificate.GetExpirationDateString()
write-host $expDate
$expDate = get-date $expDate -Format "MM/dd/yyyy HH:mm:ss"
$certExpDate = [datetime]::ParseExact($expDate, "MM/dd/yyyy HH:mm:ss", $null)
[int]$certExpiresIn = ($certExpDate - $(get-date)).Days
$certName = $req.ServicePoint.Certificate.GetName()
$certThumbprint = $req.ServicePoint.Certificate.GetCertHashString()
$certEffectiveDate = $req.ServicePoint.Certificate.GetEffectiveDateString()
$certIssuer = $req.ServicePoint.Certificate.GetIssuerName()
#$site = $site.Replace("https://", "")
if ($certExpiresIn -gt $minCertAge)
{
Write-Host "$site certificate expires in $certExpiresIn days [$certExpDate]" -f Green
$message= "$site certificate expires in $certExpiresIn days [$certExpDate]"
}
else
{
$message= "$site certificate expires in $certExpiresIn days, Expiry Date: [$certExpDate]"
Write-Host "$site certificate expires in $certExpiresIn days [$certExpDate]" -f Red
}
$listOfSites += ,@($message,$certExpiresIn)
Write-Host "_____________________"`n
}
$sb = $null
$listOfSites | Sort-Object @{Expression={$_[1]}; Ascending=$True} | %{
$sb += $($_[0])
}
$html1 = "
{font-family: Arial; font-size: 13pt;}
TABLE{border: 1px solid black; border-collapse: collapse; font-size:13pt;}
TH{border: 1px solid black; background: #dddddd; padding: 5px; color: #000000;}
TD{border: 1px solid black; padding: 5px; }
Website SSL Certificate Report
"
$html2 = "
"
$body = $html1 + $sb.ToString() + $html2
Write-Host $body
#Send-MailMessage -From aaa[@]abc.com -To xyz[@]abc.com -Subject $messagetitle -BodyAsHtml -body $body -SmtpServer smtp.abc.com -Encoding UTF8