trhall chop image Thomas R. Hall Thoughts...

Migrating Let's Encrypt Clients - From Certbot to Google ACME

In my post about Using Let’s Encrypt for Automated SSL Certificates, I discussed using the official Let’s Encrypt client, formerly called letsencrypt-auto, now called certbot. While Certbot is the official client, I disliked how heavyweight it is. You have to have Python, virtualenv, and may other ancillary tools installed in order to get it running. There had to be a better way.

Fortunately, there is. Actually, are. Multiple. There are several clients available, written in a variety of tools. But I was looking for something that not only supported the original HTTP challenge method, but also the newer DNS method. I also wanted a client that didn’t require installation of lots of tooling. I wanted a simple program that was standalone and cross platform. For me, that meant looking into a client written in Go.

Google ACME

While there are other Go implementations, many of which may be more feature-rich, Google ACME is written by some Google employees. Even though it isn’t an official Google tool, it looks well-written and is maintained. The official README has great documentation for how to use acme, including setting up a new account. I won’t repeat those instructions here. Installation is literally as simple as downloading a binary.

If you’re setting up from scratch, this is very easy and is the way to go. If, however, you already generated certificates using the official certbot client, then you may want to keep your existing account and certificates.

(Optional) Migrating a Let’s Encrypt account created with certbot to Google acme

Again, this section is only necessary if you want to migrate an existing Let’s Encrypt account that is configured with certbot to use Google acme. If you don’t need to do this, skip to the next section on Certificate Generation.

There are a couple of steps in order to migrate from an existing account created with certbot to acme, since they store keys and configuration in different formats:

  1. Create account.json file
  2. Convert account key
  3. Confirm the account is set up

Create account.json file

Before we convert your account key, we need to get the basic configuration for acme set up, which includes creating the account.json file. Since this format is specific to acme, you need to create it. Since I didn’t have a sample to use, I ended up running the setup process against the Staging server first. This generated a new key and an account.json file. I then modified the json file to have the appropriate production values for my ID and the URI.

To save you the hassle, you can use the following example below and just copy/paste in your values:

{
  "URI": "https://acme-v01.api.letsencrypt.org/acme/reg/<id>",
  "Contact": [
    "mailto:example@example.com"
  ],
  "AgreedTerms": "https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf",
  "CurrentTerms": "https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf",
  "Authz": "https://acme-v01.api.letsencrypt.org/acme/new-authz",
  "Authorizations": "",
  "Certificates": "",
  "ca": ""
}

This content goes into a file named account.json, which lives, by default, in the $HOME/.config/acme/ directory.

Convert account key

Since certbot and acme use different formats to store configuration and keys, we need to convert the account key. I followed the steps under the Option 2: Use existing letsencrypt account key section from Secure Your letsencrypt Setup With acme-tiny, which uses Jon Lundy’s conv.py Python program to do the conversion.

The steps are:

$ wget -O - "https://gist.githubusercontent.com/JonLundy/f25c99ee0770e19dc595/raw/6035c1c8938fae85810de6aad1ecf6e2db663e26/conv.py" > conv.py
$ cp /etc/letsencrypt/accounts/acme-v01.api.letsencrypt.org/directory/<id>/private_key.json private_key.json
$ openssl asn1parse -noout -out private_key.der -genconf <(python conv.py private_key.json)
$ openssl rsa -in private_key.der -inform der > account.key
$ rm conv.py private_key.json private_key.der

Once this is done, you’ve decoded the key from the certbot private_key.json file and now have a standard RSA key file, account.key which is what acme uses.

Put the account.key file into the $HOME/.config/acme/ directory, which should already exist based on the earlier step.

Confirm the account is set up

Ensure that your configuration file and key are correct by running the following two commands, which will first verify your account is set up correctly, and also ensure that you’ve agreed to the latest Terms of Service.

acme whoami

Which will produce output similar to:

URI:		 https://acme-v01.api.letsencrypt.org/acme/reg/<id>
Key:		 /home/<username>/.config/acme/account.key
Contact:	 mailto:example@example.com
Terms:		 https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf
Accepted:	 yes

If you don’t see Accepted: Yes above, be sure to run the next command.

acme update -accept

Which should produce output similar to above, including the Accepted: Yes line.

Okay, now that we’ve migrated our account, let’s generate a new cerificate with it.

Certificate Generation

If you used the default settings for creating your account key and json file, you simply need to run the acme client like this:

acme cert example.com

If you want to run it and use the DNS challenge method use this instead:

acme cert -dns=true example.com

(This will require that you create a TXT record to verify your ownership of the domain, but only needs to be done once.)

When the command finishes, your new certificate and private key will be stored in the same directory as your configuration. The files will be named similar to:

example.com.crt
example.com.key

Now, all you need to do is change the nginx config to reference them.

Nginx Configuration

Update your nginx configuration (explained in more detail in a prior post) to have the following in your includes/letsencrypt-ssl file:

ssl_certificate     /home/<username>/.config/acme/example.com.crt;
ssl_certificate_key /home/<username>/.config/acme/example.com.key;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_dhparam /etc/ssl/certs/dhparam.pem;

And that’s it!