如何配置Python SSL服务器密码套件匹配客户端(无需证书)
问题解决方法
核心原因
你遇到的ssl.SSLError: [SSL: NO_SHARED_CIPHER] no shared cipher错误本质是:默认的ssl.Purpose.CLIENT_AUTH上下文要求服务器必须配置证书,而你没有提供任何证书,导致服务器无法和curl协商出可用的加密套件;同时现代curl默认禁用了匿名TLS套件,就算你指定了普通套件,没有证书也无法完成握手。
方案1:使用匿名TLS套件(无需证书)
修改代码使用支持匿名加密的上下文,同时curl需要显式允许匿名套件:
修改后的代码
import socket, ssl # 创建支持匿名套件的上下文 context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) # 启用匿名加密套件 context.set_ciphers("ADH-AES256-GCM-SHA384:ADH-CHACHA20-POLY1305-SHA256") # 关闭客户端证书验证(因为是匿名模式) context.verify_mode = ssl.CERT_NONE bindsocket = socket.socket() bindsocket.bind(("", 443)) bindsocket.listen(5) while True: newsocket, fromaddr = bindsocket.accept() connstream = context.wrap_socket(newsocket, server_side=True) try: data = connstream.recv(1024) if not data: break # 可选:返回简单HTTP响应让curl能看到结果 connstream.send(b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!") except ssl.SSLError as e: print(f"SSL错误: {e}") finally: try: connstream.shutdown(socket.SHUT_RDWR) except: pass connstream.close()
对应的curl命令
需要加上--ciphers参数指定匿名套件,同时因为没有证书,要跳过证书验证:
curl -v --insecure --ciphers ADH-AES256-GCM-SHA384 https://localhost:443/
方案2:自动生成临时自签名证书(无需手动准备证书文件)
如果你不想用匿名套件(匿名套件安全性较低),可以在代码中自动生成临时的自签名证书,不需要提前创建证书文件:
修改后的代码
import socket, ssl from cryptography import x509 from cryptography.x509.oid import NameOID from cryptography.hazmat.primitives import serialization, hashes from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.backends import default_backend import datetime # 生成临时RSA密钥对 private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend() ) # 生成自签名证书 subject = issuer = x509.Name([ x509.NameAttribute(NameOID.COUNTRY_NAME, "CN"), x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Beijing"), x509.NameAttribute(NameOID.LOCALITY_NAME, "Beijing"), x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Test Org"), x509.NameAttribute(NameOID.COMMON_NAME, "localhost"), ]) cert = x509.CertificateBuilder().subject_name( subject ).issuer_name( issuer ).public_key( private_key.public_key() ).serial_number( x509.random_serial_number() ).not_valid_before( datetime.datetime.utcnow() ).not_valid_after( datetime.datetime.utcnow() + datetime.timedelta(days=365) ).add_extension( x509.SubjectAlternativeName([x509.DNSName("localhost")]), critical=False, ).sign(private_key, hashes.SHA256(), default_backend()) # 创建SSL上下文并加载生成的证书和密钥 context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) context.load_cert_chain( certfile=None, keyfile=None, cert_data=cert.public_bytes(serialization.Encoding.PEM), key_data=private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption() ) ) bindsocket = socket.socket() bindsocket.bind(("", 443)) bindsocket.listen(5) while True: newsocket, fromaddr = bindsocket.accept() connstream = context.wrap_socket(newsocket, server_side=True) try: data = connstream.recv(1024) if not data: break # 返回HTTP响应 connstream.send(b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!") except ssl.SSLError as e: print(f"SSL错误: {e}") finally: try: connstream.shutdown(socket.SHUT_RDWR) except: pass connstream.close()
对应的curl命令
只需要跳过证书验证即可(因为是自签名证书):
curl -v --insecure https://localhost:443/
注意事项
- 匿名TLS套件的安全性较低,仅适合测试场景,生产环境绝对不能使用。
- 自动生成的自签名证书仅用于测试,生产环境需要使用受信任的CA颁发的证书。
内容的提问来源于stack exchange,提问作者GopherM
相关产品推荐
相关产品推荐

