Test web server SSL/TLS protocol support with PowerShell

Recently I was tasked to configure SSL/TLS protocols and cipher suites for internal web servers via Group Policy. At first, we collected a list of web server and web client applications to determine the weakest possible SSL/TLS protocols. Once the list was complete, we deployed sample policy in test OU and finally applied them to the rest domain.

Now I was tasked to scan web servers to determine if they match new security policy. In order to minimize my effort in testing, I wrote a simple PowerShell script that accepts a list of web URLs and tests each host with a list of SSL protocols: SSLv2, SSLv3, TLS 1.0, TLS 1.1 and TLS 1.2. Here is a sample code:

function Test-ServerSSLSupport {
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [UInt16]$Port = 443
    process {
        $RetValue = New-Object psobject -Property @{
            Host = $HostName
            Port = $Port
            SSLv2 = $false
            SSLv3 = $false
            TLSv1_0 = $false
            TLSv1_1 = $false
            TLSv1_2 = $false
            KeyExhange = $null
            HashAlgorithm = $null
        "ssl2", "ssl3", "tls", "tls11", "tls12" | %{
            $TcpClient = New-Object Net.Sockets.TcpClient
            $TcpClient.Connect($RetValue.Host, $RetValue.Port)
            $SslStream = New-Object Net.Security.SslStream $TcpClient.GetStream()
            $SslStream.ReadTimeout = 15000
            $SslStream.WriteTimeout = 15000
            try {
                $RetValue.KeyExhange = $SslStream.KeyExchangeAlgorithm
                $RetValue.HashAlgorithm = $SslStream.HashAlgorithm
                $status = $true
            } catch {
                $status = $false
            switch ($_) {
                "ssl2" {$RetValue.SSLv2 = $status}
                "ssl3" {$RetValue.SSLv3 = $status}
                "tls" {$RetValue.TLSv1_0 = $status}
                "tls11" {$RetValue.TLSv1_1 = $status}
                "tls12" {$RetValue.TLSv1_2 = $status}
            # dispose objects to prevent memory leaks

and the usage is pretty simple:

[↓] [vPodans] "www.inbox.lv","www.paypal.com","ib.dnb.lv" | Test-ServerSSLSupport

HashAlgorithm : 32781
KeyExhange    : 44550
TLSv1_1       : True
SSLv3         : True
Host          : www.inbox.lv
SSLv2         : False
Port          : 443
TLSv1_0       : True
TLSv1_2       : True

HashAlgorithm : 32781
KeyExhange    : RsaKeyX
TLSv1_1       : True
SSLv3         : False
Host          : www.paypal.com
SSLv2         : False
Port          : 443
TLSv1_0       : True
TLSv1_2       : True

HashAlgorithm : Sha1
KeyExhange    : RsaKeyX
TLSv1_1       : False
SSLv3         : False
Host          : ib.dnb.lv
SSLv2         : False
Port          : 443
TLSv1_0       : True
TLSv1_2       : False

[↓] [vPodans]

By using this script I can easily generate the report and run it on a regular basis from scheduled task. There are places to improve, but it is already a good start.



Carl Reid
Carl Reid 18.12.2015 19:37 (GMT+3)

This looks very useful indeed! Thanks.


Why do we see the hash algorithm come back as 32781 when it is SHA256 rather than text?

Is there some enum that does not have a descriptor for SHA256 somewhere in .NET?


Harm ter Veer
Harm ter Veer 09.03.2016 09:34 (GMT+3)

Very usefull, thanks!

I added an extra try / catch on line 23 ( $TcpClient.Connect statement) on my local copy, to prevent PS from blassting errors if the server does not support HTTPS :)

Sanjay Kumar
Sanjay Kumar 05.12.2016 19:47 (GMT+3)

I am trying to install Microsoft NDES with customized templates:
1. CEP Encryption
2. Exchange Enrollment Agent (Offline Request).

I have already created required users accounts in Active Directory and assigned them the required permissions as per the Microsoft’s article:

Though I am able to install the NDES role but it is not using the templates that I customized. It is using the default templates. I’ll really appreciate if you can help me fix this issue. I want to install the NDES using the Customized templates.

Kirt Carson
Kirt Carson 24.01.2017 20:34 (GMT+3)

I needed additional information so I added the lines below to the function.

"From "+ $TcpClient.client.LocalEndPoint.address.IPAddressToString +" to $hostname "+ $TcpClient.client.RemoteEndPoint.address.IPAddressToString +':'+$TcpClient.client.RemoteEndPoint.port
$SslStream |gm |?{$_.MemberType -match 'Property'}|Select-Object Name |%{$_.Name +': '+ $sslStream.($_.name)}


From to apps.oakgov.com
CanRead: True
CanSeek: False
CanTimeout: True
CanWrite: True
CheckCertRevocationStatus: False
CipherAlgorithm: Aes256
CipherStrength: 256
HashAlgorithm: 32781
HashStrength: 384
IsAuthenticated: True
IsEncrypted: True
IsMutuallyAuthenticated: False
IsServer: False
IsSigned: True
KeyExchangeAlgorithm: 44550
KeyExchangeStrength: 256
LeaveInnerStreamOpen: False
ReadTimeout: 15000
RemoteCertificate: System.Security.Cryptography.X509Certificates.X509Certificate
SslProtocol: Tls12
TransportContext: System.Net.SslStreamContext
WriteTimeout: 15000


Joel Newton
Joel Newton 28.02.2017 18:25 (GMT+3)

Cheers, Vadim. Thanks for sharing!


tony 16.08.2017 00:03 (GMT+3)

added the folowing to handle newer Key exchange algorithm

switch ($retvalue.KeyExhange) {
            "44550" {$RetValue.KeyExhange = "ECDH_Ephem"}

Helen 31.08.2017 00:50 (GMT+3)

Thanks for the handy script. I had a small issue, just mentioning it in case this helps someone else... I was getting TLS 1.0 reporting true and TLS 1.1 and 1.2 reporting false, despite being sure I had the settings right for TLS 1.2... then I removed the catch{} and discovered the enumeration values weren't quite right for my server (Windows Server 2008 R2).

Cannot convert argument "2", with value: "tls12", for "AuthenticateAsClient" to type "System.Security.Authentication.SslProtocols": "Cannot convert value "tls12" to type "System.Security.Authentication.SslProtocols" due to invalid enumeration values. Specify one of the following enumeration values and try again. The possible enumeration values are "None, Ssl2, Ssl3, Tls, Default"."

So, "tls11" and "tls12" aren't an option. I haven't yet figured out how to find out about support for the specific versions of TLS.