如何在Python Socket服务器上配置多SSL证书及SNI支持
Python SSL实现多域名SNI配置方案
问题根源
你之前尝试的两种方法都无法实现多域名证书动态切换:重复调用load_cert_chain会直接覆盖之前的证书配置;合并证书文件后,服务器只会优先使用第一个匹配的证书,无法根据客户端请求的域名动态选择。
SNI实现步骤
Python的ssl模块从3.2版本开始支持服务器端SNI配置,核心是通过自定义SNI回调函数,在SSL握手阶段捕获客户端发送的域名,动态加载对应证书。
1. 预加载所有域名的证书上下文
先把每个域名的证书和密钥对应起来,预生成各自的SSL上下文:
import ssl import socket # 映射域名到对应的证书、密钥文件路径 domain_cert_map = { "domain1.com": ("domain1_cert.pem", "domain1_key.pem"), "domain2.com": ("domain2_cert.pem", "domain2_key.pem") } # 为每个域名预创建并加载SSL上下文 domain_contexts = {} for domain, (cert_path, key_path) in domain_cert_map.items(): ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ctx.load_cert_chain(certfile=cert_path, keyfile=key_path) domain_contexts[domain] = ctx
2. 编写SNI回调函数
这个函数会在SSL握手的Client Hello阶段被触发,根据客户端发送的域名返回对应的SSL上下文:
def sni_handler(sock, server_name, ssl_context): # 根据请求的域名匹配对应的上下文 if server_name in domain_contexts: sock.context = domain_contexts[server_name] else: # 域名未匹配时,可返回默认证书或直接终止连接 sock.context = domain_contexts.get("domain1.com") # 用domain1作为默认 fallback
3. 配置并启动SNI服务器
创建基础套接字,绑定SNI回调后再进行SSL包装:
# 创建TCP监听套接字 server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_sock.bind(("0.0.0.0", 443)) server_sock.listen(5) # 创建基础SSL上下文(仅作为占位,实际会被SNI回调替换) base_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) # 绑定SNI回调函数 base_context.sni_callback = sni_handler print("SNI SSL Server running on port 443...") while True: client_sock, client_addr = server_sock.accept() # 执行SSL握手,此时SNI回调会自动触发并切换上下文 ssl_sock = base_context.wrap_socket(client_sock, server_side=True) # 处理SSL连接逻辑 try: req_data = ssl_sock.recv(1024) print(f"Received from {client_addr}: {req_data.decode()}") ssl_sock.send(b"Response from SNI-enabled server") finally: ssl_sock.close()
关于"提前获取客户端证书需求"的说明
客户端的SNI信息是在SSL握手的Client Hello阶段发送的,这个过程发生在wrap_socket调用内部,因此无法在wrap_socket执行前获取到服务器名称。Python的SNI回调机制正是在握手过程中捕获该信息,动态替换SSL上下文,从而实现多域名证书的动态切换。
内容的提问来源于stack exchange,提问作者user1618465
相关产品推荐
相关产品推荐

