Skip to content

Instantly share code, notes, and snippets.

@jstangroome
Last active December 8, 2023 21:38
Show Gist options
  • Star 28 You must be signed in to star a gist
  • Fork 12 You must be signed in to fork a gist
  • Save jstangroome/5945820 to your computer and use it in GitHub Desktop.
Save jstangroome/5945820 to your computer and use it in GitHub Desktop.
PowerShell script to retrieve the public X509 certificate from a remote TLS endpoint
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[string]
$ComputerName,
[int]
$Port = 443
)
$Certificate = $null
$TcpClient = New-Object -TypeName System.Net.Sockets.TcpClient
try {
$TcpClient.Connect($ComputerName, $Port)
$TcpStream = $TcpClient.GetStream()
$Callback = { param($sender, $cert, $chain, $errors) return $true }
$SslStream = New-Object -TypeName System.Net.Security.SslStream -ArgumentList @($TcpStream, $true, $Callback)
try {
$SslStream.AuthenticateAsClient('')
$Certificate = $SslStream.RemoteCertificate
} finally {
$SslStream.Dispose()
}
} finally {
$TcpClient.Dispose()
}
if ($Certificate) {
if ($Certificate -isnot [System.Security.Cryptography.X509Certificates.X509Certificate2]) {
$Certificate = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList $Certificate
}
Write-Output $Certificate
}
@hertus
Copy link

hertus commented Jan 23, 2020

thanks

@msajeesh
Copy link

msajeesh commented Sep 13, 2020

This works perfectly but we have to enter server names manually..how to find out SSL certificate installed servers in a domain ?

Copy link

ghost commented Nov 5, 2020

great, thank you!

@euyuil
Copy link

euyuil commented Jan 7, 2021

Thanks! Do this work for SNI?

@jstangroome
Copy link
Author

@euyuil if line 23 $SslStream.AuthenticateAsClient('') is modified to pass a SNI host name instead of the empty string as its argument, it should work, but I have not verified.

@sahmedz11
Copy link

Is there a timeout value or validation of certificate?

@jstangroome
Copy link
Author

@sahmedz11 As it is currently written, the script will use default .NET timeouts. You can augment the script to set $TcpClient.ReceiveTimeout or .SendTimeout to manage timeouts during the TLS handshake. There is also $SslStream.ReadTimeout and .WriteTimeout which could be set, but I'm not sure if these apply during the AuthenticateAsClient() call.

To implement a custom TCP connection handshake timeout, significant changes would be required to replace $TcpClient.Connect(...) with $TcpClient.BeginConnect(...) instead and implement an AsyncCallback which is possible in PowerShell but not something I have tried.

The script also very explicitly does not perform any certificate validation, its purpose is to return the certificate for deeper inspection, valid or not. However you can modify the $Callback = { ... } line to capture and report any validation errors, or perform additional custom validation.

@ericcsinger
Copy link

Just wanted to say this is awesome, thanks!

@Grundel1979
Copy link

Thank you!. This is great!

@richardhicks
Copy link

Would it be possible to save the certificate to a file as well?

@jstangroome
Copy link
Author

@richardhicks you can call .Export(...) on the returned certificate object, specify the desired certificate file format as the argument, then pass the output bytes to Set-Content, e.g.:

$cert=(Get-RemoteSSLCertificate.ps1 foo.com)
Set-Content file.cer -Encoding Byte -Value $cert.Export('Cert')

Untested.

@richardhicks
Copy link

Thanks. I'll give it a try. Also, confirmed that adding the hostname to $SslStream.AuthenticateAsClient('') (e.g., $SslStream.AuthenticateAsClient('foo.example.net') works for sites configured with SNI.

@spawnmarvel
Copy link

Thanks

@scarson
Copy link

scarson commented Sep 26, 2023

I modified my copy of the script to write the certificate to file with:

$CertExportDirectory = 'C:\temp\'
$CertExportName = $ComputerName + '_Port' + $Port.ToString() + '.cer'
$FilePath = Join-Path $CertExportDirectory  -Child $CertExportName
Export-Certificate -Cert $Certificate -FilePath $FilePath

With output like "C:\temp\example.com_Port443.cer"

I also run a validation check with:
Test-Certificate $Certificate

You may want to set some of the non-default Test-Certificate parameters depending on use case.

@goadeff
Copy link

goadeff commented Nov 1, 2023

Any ideas how to adapt this for retrieval of the certificate used by a remote SQL server (i.e. port 1433)? The script hangs at $SslStream.AuthenticateAsClient('') when I try to use it this way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment