【问题标题】:How to communicate with a server using a TLS connection?如何使用 TLS 连接与服务器通信?
【发布时间】:2020-08-09 12:53:48
【问题描述】:

我正在使用 python 和 SSL 模块建立与服务器的 TLS 连接。然后我想向这个服务器发送一些数据。为此,我在一个名为“cert.pem”的文件中有一个CA-Certificate、客户端证书和客户端证书密钥。虽然我的服务器代码工作正常,但我的客户端代码无法连接到服务器。我只是模仿了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] 证书验证失败:无法获取本地颁发者证书 (_ssl.c:1091)

但是,当我简单地使用this command 时,我可以发送数据:

openssl s_client -connect 127.0.0.1:10023

问题

  • 我的客户端代码有什么问题?
  • 为什么openssl s_client 命令有效而python 代码无效?

我非常感谢任何改进代码的建议。

【问题讨论】:

  • 您尝试连接到端口3335,但您的服务器侦听10023。 ;)

标签: python-3.x ssl openssl


【解决方案1】:

我在这里看到两个问题:

a) 根据您的代码,您的服务器侦听端口10023,但您的客户端尝试连接到3335。但这可能只是您发布的代码中的一个问题。如果它在您运行的代码中,您根本无法建立连接。

b) 当客户端无法验证服务器证书时,您会得到一个CERTIFICATE_VERIFY_FAILED。例如,当您浏览到https://stackoverflow.com 时,您会在地址栏中看到某种锁定符号,表明您的连接是安全的。这是因为服务器提供了对域stackoverflow.com 有效且受信任的证书。在您的情况下,您的证书很可能既不有效也不可信。
为此,证书需要将 IP 地址或主机名(在 wrap_socket 方法中提供为 server_hostname 的任何内容)作为 SAN,并且证书需要由受信任的 CA 签名。由于您可能使用的是自签名证书,因此情况并非如此,因为您的信任库是 /etc/ssl/certs/ca-bundle.crt。当您颁发自签名证书时,您可以将 IP/主机名添加到 SAN 和 CN,并且在您的代码中,您可以尝试将证书添加到 /etc/ssl/certs/ca-bundle.crt。
出于开发目的,您可以将verify_mode 设置为CERT_NONE(请参阅doc),这将暂时解决您的所有问题。不过,这不应该在生产中使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-13
    • 2020-03-21
    • 2015-05-21
    • 2014-03-22
    • 1970-01-01
    • 2015-10-17
    • 2021-05-24
    相关资源
    最近更新 更多