用 ssl.SSLContext 比 ssl.wrap_socket 更可靠,因后者已弃用、不校验证书链/SNI/主机名;应使用 create_default_context() 并显式 check_hostname=True,调用 do_handshake() 后再 getpeercert(),用 cert_time_to_seconds() 解析有效期,捕获 SSLCertVerificationError 时须检查 reason 字段而非仅异常类型。

用 ssl.SSLContext 做握手比直接调 ssl.wrap_socket 更可靠
Python 3.7+ 中 ssl.wrap_socket 已被标记为弃用,它绕过证书验证逻辑、不支持 SNI、默认不校验主机名,容易误判“连接成功=证书有效”。真实场景里,你得模拟浏览器行为:发起 TLS 握手 + 验证证书链 + 检查有效期 + 核对域名。
-
ssl.create_default_context()自动启用证书验证、CRL/OCSP(若系统支持)、SNI 和主机名匹配,是安全起点 - 必须显式调用
context.check_hostname = True(默认是True,但显式写出来防误改) - 若要检查自签名或私有 CA 证书,需用
context.load_verify_locations(cafile="..."),不能靠系统默认路径 - 避免用
context.verify_mode = ssl.CERT_NONE—— 这等于关掉所有证书检查,只测 TCP 连通性
抓取证书信息得在握手后立刻调 sock.getpeercert()
证书数据不是连接建立时就拿到的,而是在 TLS 握手完成、且对方发送了 Certificate 消息之后才可读取。如果在 connect() 后马上调用 getpeercert(),会返回空或抛 ValueError;必须等握手完成——也就是调完 context.wrap_socket(sock) 并完成首次 I/O(哪怕只是 send(b""))。
- 正确顺序:
sock.connect()→wrapped = context.wrap_socket(sock)→wrapped.do_handshake()(显式触发,更可控)→wrapped.getpeercert() -
getpeercert(binary_form=True)返回 DER 字节流,可用于后续解析;binary_form=False(默认)返回字典,含notAfter、subject、issuer等字段 -
notAfter是字符串如"Jan 15 12:34:56 2025 GMT",需用ssl.cert_time_to_seconds()转成时间戳比对,别自己 parse
ssl.SSLCertVerificationError 不代表证书过期,得看 reason 字段
常见错误类型都继承自 ssl.SSLError,但只有 ssl.SSLCertVerificationError 表示证书验证失败。它的 reason 属性才是关键线索,比如 "CERTIFICATE_VERIFY_FAILED" 是根证书缺失,"CERT_HAS_EXPIRED" 才是真过期。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 捕获异常后,优先打印
e.reason和e.verify_code(如ssl.X509_V_ERR_CERT_HAS_EXPIRED == 10) - 不要仅靠异常类型做判断:
ssl.SSLCertVerificationError可能因域名不匹配、吊销、签名算法不支持等多种原因抛出 - 若想区分“过期”和“其他失败”,建议用
ssl.cert_time_to_seconds(cert["notAfter"]) < time.time()主动检查,而非依赖异常
超时和重试逻辑必须套在 socket 层,不是 SSL 层
SSL 握手卡住(比如服务端 TLS 版本不兼容、中间设备拦截)时,wrap_socket 或 do_handshake 可能长时间阻塞。Python 的 socket.settimeout() 对底层 TCP 生效,但对 SSL 握手中的读写操作不一定及时中断 —— 尤其在旧版本中。
立即学习“Python免费学习笔记(深入)”;
- 务必在创建
socket后立刻设超时:sock.settimeout(10.0),而不是等 wrap 完再设 - 避免用
context.wrap_socket(sock, server_hostname=...)一步到位,它内部可能重试多次;拆成wrap_socket+do_handshake,方便单独控制握手超时 - 重试建议最多 1–2 次,且换不同 TLS 版本(如先
TLSv1_2,失败后降级到TLSv1_1),别无脑重连 —— 很多过期证书问题本质是服务端配置陈旧
do_handshake() 显式调用,或误信 wrap_socket 返回即成功,是最常踩的坑。

















