Good CA certificate doesn't work in establishing connection in mbed_tls

I have set up an http server on my local host. I can connect to it securely using the Python requests library but when I try to do so using TLSSocket, the connection is not completed. I cannot tell why but I suspect it’s my certificate

Note that the code is correct for connecting to a remote server (I’ve been using it that way for a while but now I need to replace the remote with a local server for testing) so I think the code is correct but my certificate is wrong.

Here’s how I set it up:

certauth myrootca.pem --hostname "localhost" -d ./certs_dir

This created two files:
myrootca.pem
./certs_dir/localhost.pem

effectively creating my own CA as well as a server private key/public certificate pair.

Each of these files contain two sets of data. One set is surrounded by

-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----

and the other is surrounded by

-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----

I can just run with these files but I chose to separate them into two files: a .key file (for PRIVATE KEY) and a .crt file (for CERTIFICATE) to create

myrootca.key
myrootca.crt
./certs_dir/localhost.key
./certs_dir/localhost.crt

From what I’ve understood from what I’ve read…

The server

  • Sends the public certificate to the client when trying to create a connection
  • The certificate contains its public key
  • The certificate also contains a signature that was created using the CA private key

The client:

  • uses the CA public certificate to verify the signature in the server certificate
  • the CA cert can be used to do this since it contains the public key corresponding to the CA private key used to create the signature
  • can use the server public key (once verified) to encrypt the handshake used to establish a secure connection

Then I started an http server using uwsgi:

uwsgi --master --https-socket localhost:5683,./certs_dir/localhost.crt,./certs_dir/localhost.key --mount /=server:app

which relies on server.py:

from flask import Flask, send_from_directory
app = Flask(__name__)
@app.route("/<path:path>")
def get_root(path):
    return send_from_directory('.', path)

In a different terminal, I then ran:

python3 client.py myrootca.crt testfile

and here are the contents of client.py

# client.py
import requests
import sys

def get_file(fname):
    url = "https://localhost:5683/{}".format(fname)
    response = requests.get(url,
                            verify=sys.argv[1])
    print(f"{response.text}")

if __name__ == "__main__":
    get_file(sys.argv[2])

And then the contents of testfile are dumped to the terminal.

I also ran

openssl verify -CAfile ../myrootca.pem localhost.pem

which returns

localhost.pem: OK

to verify that my certificate was correct. Everything seems to be going well.

Then I plugged in the contents of myrootca.crt into my code. I believe the salient calls are as follows:

  TLSSocket socket
  nsapi_error_t network_error;

  network_error = socket->open(WiFiInterface::get_default_instance());
  if (network_error != NSAPI_ERROR_OK){
    INET_OTA_LOG("failed to open socket\n");
    return inet_ota_client_error_network;
  }

  ParsedUrl parsed_url = ParsedUrl(server_url);

  char *expected_schema = "https";

  if (strcmp(parsed_url.schema(), expected_schema) != 0){
    INET_OTA_LOG("expected https based ota url\n");
    return inet_ota_client_error_unknown;
  }

  if (parsed_url.host() == NULL || parsed_url.host()[0] == '\0'){
    INET_OTA_LOG("failed to parse host\n");
    return inet_ota_client_error_unknown;
  }

This is where my CA certificate comes in (myrootca.crt). I’ve also tried using localhost.crt. I’ve tried with and without newlines but it doesn’t seem to work since the subsequent connect calls fails.

  network_error = socket->set_root_ca_cert(server_cert);
  if (network_error != NSAPI_ERROR_OK){
    INET_OTA_LOG("bad server cert\n");
    return inet_ota_client_error_unknown;
  }

  size_t desired_tls_mfl = 4096;
  unsigned char desired_tls_mfl_mbed_code = MBEDTLS_SSL_MAX_FRAG_LEN_4096;

  mbedtls_ssl_config* ssl_config = socket->get_ssl_config();
  int mfl_res = mbedtls_ssl_conf_max_frag_len(ssl_config, desired_tls_mfl_mbed_code);
  if (mfl_res != 0){
    INET_OTA_LOG("failed to set tls mfl\n");
    return inet_ota_client_error_unknown;
  }

  INET_OTA_LOG("connecting to host: %s port:%u\n", parsed_url.host(), parsed_url.port());
  socket->set_timeout(20000); // 20s

This is where the code gets stuck and the error is reported

  network_error = socket->connect(parsed_url.host(), parsed_url.port());
  if (network_error != NSAPI_ERROR_OK) {
    INET_OTA_LOG("socket connect error %d\n", network_error);
    return inet_ota_client_error_network;
  }

  return inet_ota_client_error_none;

Did I use the correct certificates/keys? What else am I doing wrong?

Well…I’m not paying attention well enough. The first obvious thing I was doing wrong was using the wrong URL. After fixing that and some more testing, I believe my router is blocking traffic. Once that’s squared away, it will probably work.

Okay – closing the loop on this. Replace “localhost” above with an IP address that is reachable from your device and be sure your parsed_url.host matches that IP address. All pretty obvious but might as well make this posting as complete as possible.