2

I am using python and SSL module to establish a TLS connection to a server. Then I would like to send some data to this server. To do this, I have a CA-Certificate, Client Certificate, and Client Certificate Key in a file called "cert.pem". Although my server code works fine, my client code fails to connect to the server. I just mimicked the SSL module example and I do not understand why it fails.

The server code that works fine:

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()

The client code that fails:

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()

When I run the client code, I get this error:

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1091)

However when I simply use this command, I am able to send data:

openssl s_client -connect 127.0.0.1:10023

Questions

  • What is wrong with my client code?
  • Why the openssl s_client command works but the python code does not?

I highly appreciate any suggestions to improve the code.

Ali
  • 1,023
  • 1
  • 9
  • 31

1 Answers1

1

I see two problems here:

a) According to your code, your server listens on port 10023, but your client tries to connect to 3335. But that is probably just a problem in the code you posted. If it was in the code you run, you would not have been able to establish a connection at all.

b) You get a CERTIFICATE_VERIFY_FAILED when the server certificate can not be verified by the client. For example, when you browse to https://stackoverflow.com you will see some sort of lock-symbol in your address bar, indicating, that your connection is secure. That is because the server supplies a certificate that is valid and trusted for the domain stackoverflow.com. In your case, your certificate is most probably neither valid, nor trusted.
For that to work, the certificate would need to have the IP address or hostname (whatever you supply as server_hostname in the wrap_socket method) as SAN and the certificate would need to be signed by a trusted CA. As you are probably using a self-signed certificate, this will not be the case, because your trust-store is /etc/ssl/certs/ca-bundle.crt. When you issue your self-signed certificate, you can add the IP/hostname to SAN and CN and in your code, you can try to add the certificate to /etc/ssl/certs/ca-bundle.crt.
For development purposes, you can set verify_mode to CERT_NONE (see doc) which should solve all your problems for the moment. This should not be used in production, though.

toydarian
  • 4,246
  • 5
  • 23
  • 35