Skip to content

Instantly share code, notes, and snippets.

@vkosuri
Last active September 6, 2024 06:10
Show Gist options
  • Save vkosuri/5fd5d6b79d46700eb754aa0d18edbe62 to your computer and use it in GitHub Desktop.
Save vkosuri/5fd5d6b79d46700eb754aa0d18edbe62 to your computer and use it in GitHub Desktop.
Enabling TLS 1.1 with OpenSSL3 and Python3

Enabling TLS 1.1 with Python3 and OpenSSL3

This GIST trying to demonstrates how to set up a Python server and client to support TLS 1.1, including the necessary configurations to bypass the default security restrictions.

Background

Due to security concerns https://bugs.python.org/issue43998 and python/cpython#25778, TLS 1.0 and TLS 1.1 are deprecated and disabled by default in many environments. However, there are scenarios where you might need to support these older protocols. This GIST trying to provides a workaround to enable TLS 1.1 using Python and OpenSSL.

Important Note

As mentioned in this issue openssl/openssl#13299, in FIPS mode, the default cipher suite does not support TLS 1.1. However, OpenSSL 3.0 still supports TLS 1.1 and Python can also support TLS 1.1 with a warning. We don't need to rebuild Python; we just need to adjust the security level.

Tested Versions

  • Python: 3.10.12
  • OpenSSL: 3.0.2

Setup

Generate a Self-Signed Certificate

First, generate a self-signed certificate and a private key:

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

Start the Python Server

Run the server.py script to start the server:

python server.py

Run the Python Client

Run the client.py script to connect to the server:

python client.py

Verify Supported Ciphers

You can query OpenSSL to see the supported ciphers for TLS 1.1:

openssl ciphers -v | grep TLSv1

Example output:

ECDHE-ECDSA-AES256-SHA         TLSv1   Kx=ECDH     Au=ECDSA Enc=AES(256)               Mac=SHA1
ECDHE-RSA-AES256-SHA           TLSv1   Kx=ECDH     Au=RSA   Enc=AES(256)               Mac=SHA1
ECDHE-ECDSA-AES128-SHA         TLSv1   Kx=ECDH     Au=ECDSA Enc=AES(128)               Mac=SHA1
ECDHE-RSA-AES128-SHA           TLSv1   Kx=ECDH     Au=RSA   Enc=AES(128)               Mac=SHA1

Conclusion

By setting the security level to 0, you can enable support for TLS 1.1 in both Python and OpenSSL. This setup is useful for testing and legacy systems that still require these older protocols.

import ssl
import socket
import sys
# Create an SSL context with TLS client protocols
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
# Explicitly enable only TLS 1.1
context.minimum_version = ssl.TLSVersion.TLSv1_1
context.maximum_version = ssl.TLSVersion.TLSv1_1
# Set the security level to 0 to allow older protocols
context.set_ciphers('ECDHE-RSA-AES128-SHA@SECLEVEL=0')
# Disable certificate verification for testing purposes
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
# Set up the connection
hostname = 'localhost'
port = 4433
# Create a socket and wrap it with the SSL context
sock = socket.create_connection((hostname, port))
ssl_sock = context.wrap_socket(sock, server_hostname=hostname)
try:
# Perform the handshake
ssl_sock.do_handshake()
print("Handshake successful with TLS 1.1")
sys.stdout.flush() # Force the output to flush
# Receive data from the server
data = ssl_sock.recv(1024)
print(f"Received: {data.decode()}")
except ssl.SSLError as e:
print(f"Handshake failed: {e}")
sys.stdout.flush() # Force the output to flush
finally:
ssl_sock.close()
import ssl
import socket
# Server configuration
server_address = ('localhost', 4433)
server_cert = 'cert.pem'
server_key = 'key.pem'
# Create a socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(server_address)
sock.listen(5)
# Create an SSL context
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
# Explicitly enable only TLS 1.1
context.minimum_version = ssl.TLSVersion.TLSv1_1
context.maximum_version = ssl.TLSVersion.TLSv1_1
# Set the security level to 0 to allow older protocols
context.set_ciphers('ECDHE-RSA-AES128-SHA@SECLEVEL=0')
# Load server certificate and key
context.load_cert_chain(certfile=server_cert, keyfile=server_key)
print("Server is listening on port 4433...")
while True:
client_socket, client_address = sock.accept()
print(f"Connection from {client_address}")
# Wrap the client socket with SSL
ssl_client_socket = context.wrap_socket(client_socket, server_side=True)
try:
# Perform the handshake
ssl_client_socket.do_handshake()
print("Handshake successful with TLS 1.1")
ssl_client_socket.sendall(b"Hello, TLS 1.1 client!")
except ssl.SSLError as e:
print(f"Handshake failed: {e}")
finally:
ssl_client_socket.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment