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?