PKI Hacking for Fun and Profit

Photo of Andy Doan

Posted on Jul 27, 2021 by Andy Doan

4 min read

Products often need to talk to third party services like AWS. Many times, the best bad option available is to copy some type of API key to the device's file system. As an embedded engineer, I work under a simple assumption:

Someone will eventually hack my device

This article explores a way to provision devices without API keys.

Before going into the details, let's step back and think about the things a FoundriesFactory already has in place:

  • Control of their Public Key Infrastructure (PKI)
  • Each registered device has a unique x509 keypair that can be stored in an HSM making it much harder for someone to steal.

This is how devices authenticate with the FoundriesFactory device-gateway. However, since customers control their PKI, they can build similar services for their fleet.

How it works

There are two parts to connecting a device to a new web service.

  1. The device has to trust the connection. The web server must provide an SSL certificate the device trusts. This is done one of two ways. A web service might use TLS certificates from LetsEncrypt. Alternatively, a web service could use a TLS certificate signed by your Factory's CA. In the case of the latter, the device will have a /var/sota/root.crt that can be used to validate the server's certificate.

  2. The server has to trust the device. The device must provide proof to the server that it's the owner of a client certificate (/var/sota/client.pem) that's been signed by a CA the server trusts.

Once connected, the server knows the device by its UUID (this is set in the device's certificate) and can proceed with business logic.

In Nginx terms

Roughly speaking, this can be done in Nginx by setting ssl_client_certificate to the proper certificate chain and ssl_verify_client on. The certificate chain is pretty easy to grab from the Factory. Run fioctl keys ca show and create a file with:

  • The Factory root certificate
  • The device authentication certificates

Creating a TLS certificate

You can create your own TLS certificate if you have access to the Factory's PKI files. This will be a directory with files like: factory_ca.key, factory_ca.pem, and sign_tls_csr. You can then run this script to create the files needed by Nginx:

#!/bin/sh -e
if [ $# -ne 1 ] ; then
    echo "ERROR: $0 <domain-name>"
    exit 1

cat >ca.cnf <<EOF
prompt = no
distinguished_name = dn
req_extensions = ext

CN = $domain

keyUsage=critical, digitalSignature, keyEncipherment, keyAgreement
extendedKeyUsage=critical, serverAuth

openssl ecparam -genkey -name prime256v1 | openssl ec -out $key
openssl req -new -config ca.cnf -key $key -out $cert.csr
rm ca.cnf

./sign_tls_csr $cert.csr > $cert
rm $cert.csr

cat factory_ca.pem local-ca.pem > ${chain}

Nginx configuration

A simple Nginx configuration that could serve a device looks like:

server {
        listen              443 ssl;
        server_name         <YOUR DOMAIN NAME>;
        ssl_protocols       TLSv1.2;
        ssl_certificate           /certs/<YOUR DOMAIN NAME>.pem;
        ssl_certificate_key    /certs/<YOUR DOMAIN NAME>.key;
        ssl_client_certificate /certs/<YOUR DOMAIN NAME>-ca-chain.pem;
        ssl_verify_client      on;
        ssl_prefer_server_ciphers on;
        error_log  /var/log/nginx/error.log debug;
        location / {
                return 200 'welcome';

You could then log into a device and try out the service with:

# With aklite (will use HSM if configured):
sudo aktualizr-lite get -u https://<domain-name>/

# With curl (no HSM, but handy for debug):
sudo -s
cd /var/sota
curl -v --cacert ./root.crt --cert ./client.pem --key ./pkey.pem YOUR_URL


Now we understand the "trick" for getting data securely to a device. A web service can be built to deliver API tokens or ephemeral credentials without a device having to store them. In the next articles, I'll share concrete examples built on this idea.


Related posts