Python3.6 SSL双向认证(自签名证书)为何用TLSv1.0而非TLSv1.2
Great question—let's break this down step by step, because the issue here is a mix of how Python's ssl module reports protocol information, and potential configuration gaps if you actually aren't using TLSv1.2 (though odds are you are, even if the output looks off).
First: The TLSv1/SSLv3 in your output is not the actual protocol version
That second value in your cipher tuple—'TLSv1/SSLv3'—is a legacy protocol family label from OpenSSL, not the specific TLS version being used. The cipher suite you're seeing (ECDHE-ECDSA-AES128-SHA256) is only supported in TLSv1.2 and newer, so your connection is almost certainly using TLSv1.2 already.
To confirm this for sure, add a line to your server or client code to print the actual protocol version of the established connection:
# On the server side, after accepting the connection conn = sock.accept()[0] print("Actual TLS version in use:", conn.version()) # On the client side, after connecting conn.connect(('your-server-ip', your-port)) print("Actual TLS version in use:", conn.version())
This should output TLSv1.2 directly, which clears up the confusion from the cipher tuple's family label.
If you are actually not using TLSv1.2 (unlikely, but possible): Fix your SSL context configuration
If the version() call shows something older, here's what to check and fix:
1. Verify your system's OpenSSL version
Python's ssl module relies on your system's OpenSSL library. TLSv1.2 requires OpenSSL 1.0.1 or newer. Check your version with:
python -c "import ssl; print(ssl.OPENSSL_VERSION)"
If you're on an older version, you'll need to update OpenSSL (or your Python installation, which often bundles a newer OpenSSL).
2. Explicitly enforce TLSv1.2 in your SSL context
Python 3.6's create_default_context() enables TLSv1.0+ by default, but some system configurations might restrict newer protocols. To explicitly force TLSv1.2 (and disable older, insecure versions):
Server-side context setup:
import ssl import socket # Create a context that only allows TLSv1.2 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) # Or, use default context and disable older protocols for better future-proofing # context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) # context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 # Load your ECDSA cert/key for mutual auth context.load_cert_chain(certfile='server.crt', keyfile='server.key') context.verify_mode = ssl.CERT_REQUIRED context.load_verify_locations('client.crt') # Rest of your server code...
Client-side context setup:
import ssl import socket context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) # Or use default context with old protocols disabled # context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) # context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 context.load_cert_chain(certfile='client.crt', keyfile='client.key') context.verify_mode = ssl.CERT_REQUIRED context.load_verify_locations('server.crt') # Rest of your client code...
3. Ensure your ECDSA certificate is compatible
You mentioned using a self-signed ECDSA cert—just double-check you generated it with a modern curve (like prime256v1) using a recent OpenSSL version:
# Generate ECDSA key openssl ecparam -name prime256v1 -genkey -out server.key # Generate self-signed cert openssl req -new -x509 -key server.key -out server.crt -days 365
This ensures the cert supports TLSv1.2's cipher suites.
Wrapping up
The most likely issue is that you are using TLSv1.2, but the cipher tuple's protocol family label is misleading. The conn.version() call will confirm this. If you still aren't getting TLSv1.2, updating your OpenSSL and explicitly configuring the SSL context to enforce TLSv1.2 should resolve the problem.
内容的提问来源于stack exchange,提问作者whenitrains

