-
-
Save jstangroome/5945820 to your computer and use it in GitHub Desktop.
[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 | |
} |
This works perfectly but we have to enter server names manually..how to find out SSL certificate installed servers in a domain ?
great, thank you!
Thanks! Do this work for SNI?
@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.
Is there a timeout value or validation of certificate?
@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.
Just wanted to say this is awesome, thanks!
Thank you!. This is great!
Would it be possible to save the certificate to a file as well?
@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.
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.
Thanks
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.
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.
thanks