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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
What the problem in using plain HTTP based CRL check that had to go back to TCP to pull information out?
LikeLike
For applications not running HTTP (we have some APIs for instance) and others with authentication to access, I had to use TCP instead.
LikeLike