证书验证失败后如何从Socket对象获取SSL客户端证书?
问题:SSL证书验证失败时如何获取客户端证书
我用Python编写了一个Socket服务器,连接接收逻辑如下:
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置socket为非阻塞模式 self.socket.setblocking(False) # 绑定地址和端口 self.socket.bind((self.host, int(self.port))) # 开始监听连接 self.socket.listen()
在单独的异步函数中调用:
conn, address = await self.loop.sock_accept(self.socket)
连接建立后,将其传入:
loop.connect_accepted_socket(lambda: <custom asyncio.Protocol class>, ssl=ssl_context)
当连接建立失败时,会捕获以下错误:
except ssl.SSLError as e: print(e)
其中e可能是多种SSL错误,例如:
- [SSL: WRONG_VERSION_NUMBER]
- [SSL: SSLV3_ALERT_BAD_CERTIFICATE] sslv3 alert bad certificate (_ssl.c:1091)
在这类场景下,我希望能打印出从客户端收到的证书。尝试在异常处理中调用conn.getpeercert(),但收到错误:
AttributeError: 'socket' object has no attribute 'getpeercert'
而连接成功时,可以通过以下方法从transport对象获取对等证书:
transport.get_extra_info("peercert")
想了解在证书验证失败时,是否有办法打印从客户端收到的证书。
解决方案
方法1:使用SSL上下文的verify_callback(推荐)
SSL上下文的verify_callback会在证书验证流程中触发,无论验证是否通过,都能直接获取到客户端的证书链,这是最可靠的方式。
示例代码:
import ssl def verify_cert_callback(is_valid, cert_chain): # cert_chain是证书链列表,第一个元素为客户端证书 if not is_valid: print("客户端证书验证失败,证书信息:") print(cert_chain[0]) # 返回True表示接受证书,False表示拒绝(会触发SSLError) return is_valid # 初始化SSL上下文时配置回调 ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_context.verify_mode = ssl.CERT_REQUIRED # 强制要求客户端提供证书 ssl_context.verify_callback = verify_cert_callback
当客户端证书验证失败时,回调函数会先于异常捕获执行,你可以在这里直接打印或记录证书信息。
方法2:异常处理中手动包装Socket(兼容性差,不推荐)
如果必须在SSLError的异常块中处理,可以尝试将普通Socket包装为SSLSocket,再尝试获取证书。但这种方法不稳定,因为握手失败后Socket可能处于异常状态,不一定能成功获取证书。
示例代码:
except ssl.SSLError as e: try: # 将普通Socket包装为SSLSocket ssl_sock = ssl_context.wrap_socket(conn, server_side=True) cert = ssl_sock.getpeercert() if cert: print("客户端证书:", cert) except Exception as wrap_err: print("无法获取客户端证书:", str(wrap_err)) print("SSL错误:", str(e))
内容的提问来源于stack exchange,提问作者Anirudh Panchangam
相关产品推荐
相关产品推荐

