Retrieve SSL certificate information

I recently had a need to retrieve SSL certificate information from a group of servers, running a mix of both Linux and Windows, and I didn’t really want to get the information manually.

The web applications running on these servers where a mix of “regular” web applications and applications without any front-end. Also many of them where running on shared hosts.

My first idea was to write a script that performed an HTTP web request to get the certificate object. This worked for some of the applications, but not all. So wrote another script that used TCP instead. Both of these scripts where written in PowerShell, but for good measure I wrote a small bash script as well that I had planned to use on the Linux machines.

But in the end the two PowerShell scripts seemed to be good enough for the task at hand. I’m sharing my scripts here in case they can be of use to others in a similar situation.

The usage should be easy enough, though note that Get-CertInfoHttp takes the input as a URL while Get-CertInfoTcp takes a computer name.

All three scripts should fetch the same kind of data, but feel free to customize them to your own needs. I actually ended up writing a wrapper script that first tried to get the SSL info using HTTP and then to fall back to try using TCP if that failed. This worked pretty well in my case.

Enjoy. Let me know if you have any questions of comments about the scripts.

function Get-CertInfoHttp {
[CmdletBinding()]
param (
[Parameter(Position = 0, Mandatory)]
[string] $URL,
[Parameter()]
[switch] $ReturnCertificate
)
try {
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$webRequest = [System.Net.HttpWebRequest]::Create($URL)
$webRequest.KeepAlive = $false
$webRequest.Timeout = 5000
$webRequest.ServicePoint.ConnectionLeaseTimeout = 5000
$webRequest.ServicePoint.MaxIdleTime = 5000
#$null = $webRequest.GetResponse()
$null = $webRequest.GetResponse().Dispose()
}
catch [System.Net.WebException] {
if ($_.Exception.Status -eq [System.Net.WebExceptionStatus]::TrustFailure) {
# We ignore trust failures, since we only want the certificate, and the service point is still populated at this point
}
else
{
Write-Warning $_.Exception.Message
}
}
catch {
Write-Warning $_.Exception.Message
}
if (($webRequest.ServicePoint.Certificate) -and ($webRequest.ServicePoint.Certificate.Handle -ne 0)) {
if ($ReturnCertificate) {
Write-Output $webRequest.ServicePoint.Certificate
}
else {
Write-Output ([PSCustomObject] [Ordered] @{
IssuerCN = $webRequest.ServicePoint.Certificate.Issuer.Split(', ',[System.StringSplitOptions]::RemoveEmptyEntries)[0].Split('=')[1]
SubjectCN = $webRequest.ServicePoint.Certificate.Subject.Split(', ',[System.StringSplitOptions]::RemoveEmptyEntries)[0].Split('=')[1]
ValidFrom = $webRequest.ServicePoint.Certificate.GetEffectiveDateString()
ValidTo = $webRequest.ServicePoint.Certificate.GetExpirationDateString()
})
}
$webRequest.ServicePoint.Certificate.Dispose()
}
[Net.ServicePointManager]::ServerCertificateValidationCallback = $null
}

view raw
Get-CertInfoHttp.ps1
hosted with ❤ by GitHub

function Get-CertInfoTcp {
[CmdletBinding()]
param (
[Parameter(Position = 0, Mandatory)]
[string] $ComputerName,
[Parameter(Position = 1)]
[int] $Port = 443,
[Parameter()]
[int] $Timeout = 3000,
[Parameter()]
[switch] $ReturnCertificate
)
try {
$tcpClient = New-Object TypeName System.Net.Sockets.TcpClient
$iar = $tcpClient.BeginConnect($ComputerName,$Port,$null,$null)
$wait = $iar.AsyncWaitHandle.WaitOne($Timeout,$false)
if (!$wait) {
$tcpClient.Close()
Write-Warning 'Connection attempt timed out'
}
else {
$null = $tcpClient.EndConnect($iar)
if ($tcpClient.Connected) {
$tcpStream = $tcpClient.GetStream()
$sslStream = New-Object TypeName System.Net.Security.SslStream ArgumentList ($tcpStream, $false)
$sslStream.AuthenticateAsClient($ComputerName)
$certificate = New-Object TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 ArgumentList ($sslStream.RemoteCertificate)
if ($ReturnCertificate) {
Write-Output $certificate
}
else {
Write-Output ([PSCustomObject] [Ordered] @{
IssuerCN = $certificate.Issuer.Split(', ',[System.StringSplitOptions]::RemoveEmptyEntries)[0].Split('=')[1]
SubjectCN = $certificate.Subject.Split(', ',[System.StringSplitOptions]::RemoveEmptyEntries)[0].Split('=')[1]
ValidFrom = $certificate.NotBefore
ValidTo = $certificate.NotAfter
})
}
$certificate.Dispose()
$sslStream.Close()
$sslStream.Dispose()
$tcpStream.Close()
$tcpStream.Dispose()
}
else {
Write-Warning "Unable to establish connection to $ComputerName on port $Port"
}
$tcpClient.Close()
}
}
catch {
Write-Warning $_.Exception.InnerException.Message
}
}

view raw
Get-CertInfoTcp.ps1
hosted with ❤ by GitHub

DNS_NAME="some.domain.tld"
openssl s_client -connect ${DNS_NAME}:443 -servername ${DNS_NAME} 2>/dev/null </dev/null | openssl x509 -noout -dates -issuer -subject

view raw
GetCertInfo.sh
hosted with ❤ by GitHub

2 comments

    1. For applications not running HTTP (we have some APIs for instance) and others with authentication to access, I had to use TCP instead.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s