Python – Get TLS Versions with Cipher Suites

# Author: Kerry Cordero
# Version: 1.0.0
# Description: This script prompts for domain and gets the TLS version for that domain along with Cipher Suites

import ssl
import socket
import warnings
from termcolor import colored
from prettytable import PrettyTable

# Suppress deprecation warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)

# Define a list of OpenSSL-style cipher suites
CIPHER_SUITES = [
    'DHE-RSA-AES128-GCM-SHA256',
    'DHE-RSA-AES256-GCM-SHA384',
    'ECDHE-RSA-AES128-GCM-SHA256',
    'ECDHE-RSA-AES256-GCM-SHA384',
    'ECDHE-RSA-CHACHA20-POLY1305',
    'AES128-GCM-SHA256',  
    'AES256-GCM-SHA384',  
    'DHE-RSA-AES256-SHA256',
    'ECDHE-RSA-AES128-SHA256',
    'AES256-GCM-SHA384',
    'AES128-GCM-SHA256',
    'AES256-SHA256',
    'AES128-SHA256'
    # You can add more cipher suites here...
]

# Define TLS versions
TLS_VERSIONS = {
    "TLSv1.0": ssl.PROTOCOL_TLSv1,
    "TLSv1.1": ssl.PROTOCOL_TLSv1_1,
    "TLSv1.2": ssl.PROTOCOL_TLSv1_2,
    "TLSv1.3": ssl.PROTOCOL_TLS
}


def check_cipher_suite_support(fqdn, port, cipher_suite, tls_version):
    context = ssl.SSLContext(tls_version)
    context.verify_mode = ssl.CERT_NONE
    try:
        context.set_ciphers(cipher_suite)
    except ssl.SSLError:
        # This can occur if the cipher suite is not compatible with the chosen TLS version
        return False

    try:
        with socket.create_connection((fqdn, port)) as sock:
            with context.wrap_socket(sock, server_hostname=fqdn) as ssock:
                return True
    except Exception as e:
        return False


def main():
    fqdn = input("Enter the FQDN: ")
    port = 443  # default port for HTTPS, change if necessary

    print(f"\nScanning {fqdn} for supported cipher suites...\n")

    # Create a table with TLS versions as columns
    table = PrettyTable()
    table.field_names = ["Cipher Suites"] + list(TLS_VERSIONS.keys())

    # Fill the table with cipher suite support information
    for cipher_suite in CIPHER_SUITES:
        row = [cipher_suite]
        for tls_version_name, tls_version_protocol in TLS_VERSIONS.items():
            is_supported = check_cipher_suite_support(fqdn, port, cipher_suite, tls_version_protocol)
            status = colored('✔️', 'green') if is_supported else colored('❌', 'red')
            row.append(status)
        table.add_row(row)

    print(table)


if __name__ == "__main__":
    main()

I received an email from a Vendor stating they will no longer support some older 1.2 Cipher Suites and listed the Cipher suites they support:

The following TLSv1.3 cipher suites will be supported (suites in server-preferred order).
TLS_AES_128_GCM_SHA256 (0x1301)
TLS_AES_256_GCM_SHA384 (0x1302)

The following TLSv1.2 cipher suites will be supported (suites in server-preferred order).
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027)

The following TLSv1.2 cipher suites will no longer be supported.
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
TLS_RSA_WITH_AES_128_GCM_SHA256 (0x9c)
TLS_RSA_WITH_AES_256_GCM_SHA384 (0x9d)
TLS_RSA_WITH_AES_128_CBC_SHA (0x2f)
TLS_RSA_WITH_AES_128_CBC_SHA256 (0x3c)
TLS_RSA_WITH_AES_256_CBC_SHA (0x35)
TLS_RSA_WITH_AES_256_CBC_SHA256 (0x3d)

In cases like this, you can just scan for those cipher suites:

CIPHER_SUITES = [
    'AES_128_GCM_SHA256',          # TLSv1.3
    'AES_256_GCM_SHA384',          # TLSv1.3
    'ECDHE_RSA_WITH_AES_256_GCM_SHA384', # TLSv1.2
    'ECDHE_RSA_WITH_AES_128_GCM_SHA256', # TLSv1.2
    'ECDHE_RSA_WITH_AES_256_CBC_SHA384', # TLSv1.2 (less preferred due to CBC mode)
    'ECDHE_RSA_WITH_AES_128_CBC_SHA256'  # TLSv1.2 (less preferred due to CBC mode)
]