如何使用 TLS 连接与服务器通信?
How to communicate with a server using a TLS connection?
我正在使用 python 和 SSL 模块与服务器建立 TLS 连接。然后我想向这个服务器发送一些数据。为此,我在名为“cert.pem”的文件。尽管我的服务器代码工作正常,但我的客户端代码无法连接到服务器。我只是模仿了 SSL module example 但我不明白为什么它会失败。
工作正常的服务器代码:
import socket, ssl
cert_chain = 'cert.pem'
host_addr = socket.gethostbyname(socket.gethostname())
host_port = 10023
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(certfile=cert_chain)
bindsocket = socket.socket()
bindsocket.bind((host_addr, host_port))
bindsocket.listen(5)
while True:
print("Waiting for client")
newsocket, fromaddr = bindsocket.accept()
conn = context.wrap_socket(newsocket, server_side=True)
print("SSL established. Peer: {}".format(conn.getpeercert()))
buf = b'' # Buffer to hold received client data
try:
while True:
data = conn.recv(4096)
if data: # Client sent us data. Append to buffer
buf += data
else: # No more data from client. Show buffer and close connection.
print("Received:", buf)
break
finally:
print("Closing connection")
conn.shutdown(socket.SHUT_RDWR)
conn.close()
失败的客户端代码:
import socket, ssl
server_addr = '**.***.***.***'
server_port = 3335
cert_chain = 'cert.pem'
context = ssl.create_default_context()
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations("/etc/ssl/certs/ca-bundle.crt")
conn = context.wrap_socket(socket.socket(socket.AF_INET), server_hostname=server_addr)
conn.connect((server_addr, server_port))
conn.send(b"Hello, world!")
conn.close()
当我 运行 客户端代码时,我得到这个错误:
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1091)
然而,当我简单地使用 时,我能够发送数据:
openssl s_client -connect 127.0.0.1:10023
问题
- 我的客户端代码有什么问题?
- 为什么
openssl s_client
命令有效但 python 代码无效?
我非常感谢任何改进代码的建议。
我在这里看到两个问题:
a) 根据您的代码,您的服务器侦听端口 10023
,但您的客户端尝试连接到 3335
。但这可能只是您发布的代码中的一个问题。如果它在您的代码中 运行,您将根本无法建立连接。
b) 当客户端无法验证服务器证书时,您会得到一个 CERTIFICATE_VERIFY_FAILED
。例如,当您浏览到 https://whosebug.com
时,您会在地址栏中看到某种 lock-symbol,表明您的连接是安全的。这是因为服务器提供的证书对域 whosebug.com
有效且受信任。在您的情况下,您的证书很可能既无效也不可信。
为此,证书需要将 IP 地址或主机名(无论您在 wrap_socket
方法中作为 server_hostname
提供什么)作为 SAN 并且证书需要由受信任的 CA。由于您可能使用的是 self-signed 证书,因此情况并非如此,因为您的 trust-store 是 /etc/ssl/certs/ca-bundle.crt
。当您颁发 self-signed 证书时,您可以将 IP/hostname 添加到 SAN 和 CN,并且在您的代码中,您可以尝试将证书添加到 /etc/ssl/certs/ca-bundle.crt
.
出于开发目的,您可以将 verify_mode
设置为 CERT_NONE
(参见 doc),这应该可以暂时解决您的所有问题。不过,这不应在生产中使用。
我正在使用 python 和 SSL 模块与服务器建立 TLS 连接。然后我想向这个服务器发送一些数据。为此,我在名为“cert.pem”的文件。尽管我的服务器代码工作正常,但我的客户端代码无法连接到服务器。我只是模仿了 SSL module example 但我不明白为什么它会失败。
工作正常的服务器代码:
import socket, ssl
cert_chain = 'cert.pem'
host_addr = socket.gethostbyname(socket.gethostname())
host_port = 10023
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(certfile=cert_chain)
bindsocket = socket.socket()
bindsocket.bind((host_addr, host_port))
bindsocket.listen(5)
while True:
print("Waiting for client")
newsocket, fromaddr = bindsocket.accept()
conn = context.wrap_socket(newsocket, server_side=True)
print("SSL established. Peer: {}".format(conn.getpeercert()))
buf = b'' # Buffer to hold received client data
try:
while True:
data = conn.recv(4096)
if data: # Client sent us data. Append to buffer
buf += data
else: # No more data from client. Show buffer and close connection.
print("Received:", buf)
break
finally:
print("Closing connection")
conn.shutdown(socket.SHUT_RDWR)
conn.close()
失败的客户端代码:
import socket, ssl
server_addr = '**.***.***.***'
server_port = 3335
cert_chain = 'cert.pem'
context = ssl.create_default_context()
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations("/etc/ssl/certs/ca-bundle.crt")
conn = context.wrap_socket(socket.socket(socket.AF_INET), server_hostname=server_addr)
conn.connect((server_addr, server_port))
conn.send(b"Hello, world!")
conn.close()
当我 运行 客户端代码时,我得到这个错误:
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1091)
然而,当我简单地使用
openssl s_client -connect 127.0.0.1:10023
问题
- 我的客户端代码有什么问题?
- 为什么
openssl s_client
命令有效但 python 代码无效?
我非常感谢任何改进代码的建议。
我在这里看到两个问题:
a) 根据您的代码,您的服务器侦听端口 10023
,但您的客户端尝试连接到 3335
。但这可能只是您发布的代码中的一个问题。如果它在您的代码中 运行,您将根本无法建立连接。
b) 当客户端无法验证服务器证书时,您会得到一个 CERTIFICATE_VERIFY_FAILED
。例如,当您浏览到 https://whosebug.com
时,您会在地址栏中看到某种 lock-symbol,表明您的连接是安全的。这是因为服务器提供的证书对域 whosebug.com
有效且受信任。在您的情况下,您的证书很可能既无效也不可信。
为此,证书需要将 IP 地址或主机名(无论您在 wrap_socket
方法中作为 server_hostname
提供什么)作为 SAN 并且证书需要由受信任的 CA。由于您可能使用的是 self-signed 证书,因此情况并非如此,因为您的 trust-store 是 /etc/ssl/certs/ca-bundle.crt
。当您颁发 self-signed 证书时,您可以将 IP/hostname 添加到 SAN 和 CN,并且在您的代码中,您可以尝试将证书添加到 /etc/ssl/certs/ca-bundle.crt
.
出于开发目的,您可以将 verify_mode
设置为 CERT_NONE
(参见 doc),这应该可以暂时解决您的所有问题。不过,这不应在生产中使用。