How To: Test Available TLS Versions Between a Client and Server

Follow
    Applies to:
  • SecureAuth Identity Platform
Deployment model:
  • Cloud
  • Hybrid
  • On Premises
  • Version Affected: All

     

    Overview

    This article covers two ways to test TLS (Transport Layer Security) compatibility between a client and a target server: a PowerShell script that reports which TLS protocol versions successfully negotiate a handshake, and how to use Wireshark to capture and read the handshake directly when you need to see exactly which cipher suite was negotiated, or why a handshake failed.

    There is more than one way to do this:

    • Use Method 1 first, to quickly check which TLS versions negotiate against a target host and port.
    • Use Method 2 when you need to see the actual cipher suite negotiated, or diagnose why a handshake failed.

    In this article

     

    Method 1: Test TLS Versions With the PowerShell Script

    Use this method first. It attempts TLS 1.3, TLS 1.2, TLS 1.1, and TLS 1.0 in turn against a target host and port, and reports whether each version negotiated successfully or failed — useful for confirming which TLS versions a server currently accepts, for example verifying that TLS 1.0/1.1 have actually been disabled after a Schannel hardening change, or diagnosing why an older client can no longer connect.

    Save the following script as Test-TLSVersions.ps1:

    param(
        [Parameter(Mandatory=$true)]
        [string]$target,
    
        [Parameter(Mandatory=$true)]
        [int]$port
    )
    
    # Top-down: newest to oldest
    $protocolsToTest = [ordered]@{
        'TLS 1.3' = [System.Security.Authentication.SslProtocols]::Tls13
        'TLS 1.2' = [System.Security.Authentication.SslProtocols]::Tls12
        'TLS 1.1' = [System.Security.Authentication.SslProtocols]::Tls11
        'TLS 1.0' = [System.Security.Authentication.SslProtocols]::Tls
    }
    
    foreach ($protocolName in $protocolsToTest.Keys) {
        $sslProtocol = $protocolsToTest[$protocolName]
        $tcpClient = $null
        $sslStream = $null
    
        Write-Host "`n--- Testing $protocolName against $target`:$port ---"
    
        try {
            $tcpClient = New-Object System.Net.Sockets.TcpClient
            $tcpClient.Connect($target, $port)
            $sslStream = New-Object System.Net.Security.SslStream(
                $tcpClient.GetStream(),
                $false,
                ({ $true } -as [System.Net.Security.RemoteCertificateValidationCallback])
            )
            $sslStream.AuthenticateAsClient($target, $null, $sslProtocol, $false)
            Write-Host "$protocolName negotiated successfully" -ForegroundColor Green
        }
        catch {
            $base = $_.Exception.GetBaseException()
            Write-Host "$protocolName FAILED" -ForegroundColor Red
            Write-Host "Base exception type: $($base.GetType().FullName)"
            Write-Host "Base exception message: $($base.Message)"
            Write-Host ("HResult: 0x{0:X8}" -f $base.HResult)
        }
        finally {
            if ($sslStream) { $sslStream.Dispose() }
            if ($tcpClient) { $tcpClient.Dispose() }
        }
    }

    Run it from PowerShell, passing the target hostname (or IP address) and port:

    .\Test-TLSVersions.ps1 -target <hostname> -port 443

    PowerShell output of Test-TLSVersions.ps1 run against IdP1-Pri-2404 on port 443, showing TLS 1.3 failed with a Win32Exception because the client and server do not share a common algorithm, while TLS 1.2, 1.1, and 1.0 all negotiated successfully.

    The example above shows a server where TLS 1.3 is not supported (or not enabled), while TLS 1.2, 1.1, and 1.0 all still negotiate. Each protocol that negotiates successfully is shown in green; each one that fails is shown in red, along with the underlying .NET exception type, message, and HResult to help identify the cause.

    This script only confirms whether the client and server can complete a TLS handshake for each protocol version — it does not show which cipher suite was negotiated. If a version fails and you need to know whether a specific cipher was the cause, or you need to confirm the cipher used on a successful connection, use Method 2 below.


     

    Method 2: Capture and Read the Handshake With Wireshark

    Use this method when you need to see exactly what happened during the handshake — which cipher suites the client offered, which one (if any) the server selected, or why a negotiation failed.

     

    Install Wireshark

    If it is not already installed, download Wireshark from the official site: wireshark.org/download.html.

     

    Start a Capture

    1. Open Wireshark and select the network interface the traffic will pass through (usually the primary NIC on the machine initiating the connection).
    2. Optionally, enter a capture filter before starting. A capture filter is applied while Wireshark is recording, and limits which packets it saves at all — anything that does not match is discarded immediately and cannot be recovered later. Enter one in the filter field on the start screen, for example:
      host <target> and port <port>
      This limits the capture to traffic between your machine and just the target host and port, which keeps the capture small and easy to work with. Without a capture filter, Wireshark records every packet crossing the interface, producing a much larger, noisier capture — slower to search afterward, but with the advantage that nothing outside what you thought to filter for can be missed.
    3. Click the blue shark-fin icon (or Capture > Start) to begin capturing.
    4. Reproduce the connection attempt while the capture is running — for example, re-run Test-TLSVersions.ps1 against the same target, or have the affected client attempt to connect.
    5. Click the red square icon (or Capture > Stop) once the attempt has finished.

     

    Find and Read the Conversation With a Display Filter

    A display filter is different from a capture filter: it only changes what is currently shown from packets already captured, without discarding anything, so it can be typed, cleared, and retyped as many times as needed after the capture is stopped.

    1. If you captured without a capture filter, narrow the view first by typing tls into the display filter bar (use ssl instead on older Wireshark versions) and pressing Enter. This shows only TLS handshake and application-data packets.
    2. Find a Client Hello or Server Hello frame for the target and click it, then expand Transport Layer Security in the packet details pane below the list. Note the number shown next to [Stream index: N] — Wireshark assigns every TCP connection its own stream number.
    3. To pull back the entire conversation for that one connection — every packet from the initial handshake through to its close, not just the TLS packets — type the following into the display filter bar, replacing N with the stream index you noted:
      tcp.stream eq N
      Alternatively, right-click any packet belonging to the conversation and select Follow > TCP Stream: this applies the same tcp.stream eq N filter to the packet list automatically, and also opens a separate window showing the conversation reassembled as a readable stream.

    Wireshark packet capture of a TLS 1.2 handshake. The left pane shows the Client Hello frame listing the 18 cipher suites offered by the client; the right pane shows the Server Hello frame with the single cipher suite the server selected, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, with a line connecting it back to the matching entry in the client's offered list.

    With the conversation isolated, read the negotiation from the two Hello frames: the Client Hello frame (sent by the client) lists every cipher suite the client is offering, under Handshake Protocol: Client Hello > Cipher Suites. The Server Hello frame (the server's reply) shows the single cipher suite the server selected from that list, under Handshake Protocol: Server Hello > Cipher Suite. In the example above, the server chose TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 from the 18 suites the client offered.

    If the server rejects the connection instead of replying with a Server Hello, there is no negotiated cipher to read — compare the cipher suites your client offered in its Client Hello against what the server is configured to accept. A missing or disabled cipher, rather than the TLS version itself, is a common cause of a handshake failure that Method 1 alone cannot distinguish.

    For the full list of cipher suites supported by each TLS version in Schannel (the TLS/SSL provider built into Windows), see Microsoft's Protocols in TLS/SSL (Schannel SSP) documentation.


     

    SecureAuth Knowledge Base Articles provide information based on specific use cases and may not apply to all appliances or configurations. Be advised that these instructions could cause harm to the environment if not followed correctly or if they do not apply to the current use case.

    Customers are responsible for their own due diligence prior to utilizing this information and agree that SecureAuth is not liable for any issues caused by misconfiguration directly or indirectly related to SecureAuth products.

    0 out of 0 found this helpful

    Comments

    0 comments

    Please sign in to leave a comment.