Implementing an ACME Client in C
What is ACME?
ACME (Automatic Certificate Management Environment) is a protocol specified by RFC 8555 that is widely used for obtaining Let’s Encrypt and other TLS certificates automatically. A user (ACME client machine, usually automated) can request a certificate for a domain it has control over by sending a request to an ACME server. The ACME server will then generate challenges for the client which have to be fulfilled in order to prove control over the requested domains. A challenge is a random token that the client needs to serve via HTTP or DNS TXT record. If the conditions are met and the challenges are fulfilled before timeout, the server will issue the certificate and make it available for the client.
The client
The client has to include an HTTP(S) client for making the requests to the ACME server, I use cURL for this. Most of the data exchange is done via signed, base64-encoded JSON objects (JSON Web Tokens), which guarantee data integrity assurance in addition to the transport layer security of HTTPS. A nonce-value in each token achieves protection against replay attacks. I do not use a JOSE (JSON Object Signing and Encryption) library, but instead implement that functionality manually using cJSON (a JSON parser) and OpenSSL 3.
If the client should work without the need for additional webservers, the client must be capable of serving the server’s tokens via HTTP or DNS TXT record to prove the ownership of the domain.
But why?
Initially, this was a project for a network security lecture at university. The task was to write an ACME client from scratch using your favorite programming language. I also wrote a (very minimal) custom DNS and HTTP(S) server for testing. In the meantime I have removed those from the project, because they were specific to the given testing infrastructure.
I have reconstructed a large part of the code, because I was not happy with my initial design. Some parts are still lacking some error handling and documentation, so I still have some cleanup to do in the future.
Capabilities
My client can be invoked from the command line to automatically request, validate and obtain an SSL/TLS certificate from an ACME server. It does the HTTP validation internally without using nginx or Apache which is running on the target server.
For development testing, I have used a local Pebble testing server and some unit testing scripts I wrote. I also successfully obtained a production SSL/TLS certificate from Let’s Encrypt on a fresh Ubuntu 22.10 Droplet.