Python TLS套接字:如何检测服务器由于证书失败而关闭连接?

xmd2e60i  于 2023-09-29  发布在  Python
关注(0)|答案(1)|浏览(120)

尝试使用Python 3编写TLS客户端。不知道什么是正确的方法来检测和处理连接拒绝服务器。
如果TLS服务器需要客户端证书,但客户端没有调用SSLContext.load_cert_chain()或分配了错误的证书,则服务器将终止连接。问题是,客户端在下一次调用SSLSocket.recv()之前不会检测到它。
例如,下面的代码将创建一个客户端TLS套接字并连接到服务器,并保持服务器的轮询数据:

import ssl
import socket

context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = False
# context.load_cert_chain(certfile='client.crt', keyfile='client.key')
context.load_verify_locations(cafile='ca.crt')
with socket.create_connection(('127.0.0.1', 12345)) as client:
    with context.wrap_socket(client, server_hostname='example.com') as ssock:
        ssock.sendall(b'Hello, world\n')
        while 1:
            data = ssock.recv(1024)
            if not data:
                ssock.close()
                exit()
            do_something(data)

假设服务器端在OpenSSL中运行-Verify=1

openssl s_server -port 12345 -CAfile ca.crt -cert server.crt -key server.key -Verify 1

在客户端中注解了context.load_cert_chain()后,此连接将被服务器拒绝并丢弃:

ERROR
00A77D53F87F0000:error:0A0000C7:SSL routines:tls_process_client_certificate:peer did not return a certificate:ssl/statem/statem_srvr.c:3511:
shutting down SSL
CONNECTION CLOSED

但是客户端直到在while 1循环中第一次调用ssock.recv()才知道这一点:

Traceback (most recent call last):
  File "client.py", line 13, in <module>
    data = ssock.recv(1024)
           ^^^^^^^^^^^^^^^^
  File "***/python3.11/ssl.py", line 1263, in recv
    return self.read(buflen)
           ^^^^^^^^^^^^^^^^^
  File "***/python3.11/ssl.py", line 1136, in read
    return self._sslobj.read(len)
           ^^^^^^^^^^^^^^^^^^^^^^
ssl.SSLError: [SSL: TLSV13_ALERT_CERTIFICATE_REQUIRED] tlsv13 alert certificate required (_ssl.c:2576)

在进入while 1循环之前,是否有方法检测服务器关闭的连接?

wkyowqbh

wkyowqbh1#

尝试此更改,它必须正常工作:

import ssl
import socket

context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = False
# context.load_cert_chain(certfile='client.crt', keyfile='client.key')
context.load_verify_locations(cafile='ca.crt')
with socket.create_connection(('127.0.0.1', 12345)) as client:
    with context.wrap_socket(client, server_hostname='example.com') as ssock:
        ssock.sendall(b'Hello, world\n')
        ssock.settimeout(1)  # Set a timeout for recv() in seconds
        while True:
            try:
                data = ssock.recv(1024)
                if not data:
                    ssock.close()
                    break  # Exit the loop if the connection is closed
                # Process received data here
                do_something(data)
            except ssl.SSLWantReadError:
                # Handle the case where there's no data available to read
                pass

我使用ssock.settimeout(1)在recv()调用上设置了1秒的超时。这意味着,如果在指定的超时时间内没有数据可供读取,recv()将返回None。您可以捕获ssl.SSLWantReadError异常来处理这种情况,并继续检查数据,而不会陷入recv()调用。

相关问题