You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python绕过DNS获取指定域名SSL证书遇TLS握手失败问题

问题解决:绕过DNS获取指定站点的SSL证书

核心错误分析

你的代码里连接目标和SNI参数完全搞反了:你需要连接到承载siteB.fqdn证书的服务器serverA.fqdn(或其IP),同时通过server_hostname传递siteB.fqdn触发SNI扩展,让Apache返回对应站点的证书。原代码连接siteB.fqdn却指定SNI为hostA.fqdn,服务器无法匹配到对应站点,直接抛出握手失败错误(提示里的SSLV3是通用错误标识,并非真的用SSLv3握手,Python3.12默认已禁用SSLv3)。

修正后的代码

#!/usr/bin/env python3
import pprint
import socket
import ssl

# 使用默认安全上下文,手动锁定TLS版本范围(匹配目标服务器支持的TLSv1.2/1.3)
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
context.minimum_version = ssl.TLSVersion.TLSv1_2
context.maximum_version = ssl.TLSVersion.TLSv1_3

# 创建TCP socket,连接到实际承载证书的服务器serverA.fqdn(可替换为直接IP,彻底绕过DNS)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("serverA.fqdn", 443))

# 包装socket时,通过server_hostname指定要获取证书的站点域名(SNI)
conn = context.wrap_socket(sock, server_hostname="siteB.fqdn")

try:
    # 获取证书元数据,无需发起HTTP请求
    cert = conn.getpeercert()
    pprint.pprint(cert)
finally:
    # 确保连接关闭
    conn.close()

关键配置说明

  1. SNI参数的正确使用:server_hostname必须设为你要获取证书的域名siteB.fqdn,这是Apache多站点识别证书的核心依据。
  2. 连接目标指向实际服务器:connect的参数是serverA.fqdn(或其IP),直接绕过DNS解析到目标服务器。
  3. TLS版本锁定:手动指定版本范围,避免系统默认配置出现兼容性问题,完全匹配目标服务器的支持版本。

验证逻辑参考

你提到ssl.get_server_certificate能正常工作,本质是该函数正确处理了连接目标和SNI的对应关系,等价调用如下:

cert_pem = ssl.get_server_certificate(("serverA.fqdn", 443), server_hostname="siteB.fqdn")

内容的提问来源于stack exchange,提问作者Henrik Hudson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 18:50:22