Amazon Web Services (AWS) offers a wealth of services for site owners. A service I particularly enjoy is the inexpensive CloudFront CDN which lets me deliver static content, like downloads and update information for my software, very fast to people across the world. What became apparent is that while it was fast and cheap, it wasn't the most secure solution. Anyone could forge the update response and mislead my users to downloading a modified package full of malware. The solution was to use an SSL certificate with the CDN, ensuring the integrity of the downloads and update information. For this purpose I used Let's Encrypt™ which allows you to create properly singed SSL certificates for free. The process is non-obvious so I'm documenting this for you.

Prerequisites

Before you begin you'll need the following:

  • A computer running any operating system you choose (Windows, macOS and Linux work great).
  • An Amazon AWS account with a CloudFront distribution already set up and running on your own (sub)domain.
  • The AWS Command Line client installed and configured.
  • Access to the DNS zone setup for the domain the CloudFront distribution runs on. I recommend using Amazon Route 53.

Installing an SSL certificate for the first time

The very first time you set up SSL with your CloudFront distribution you'll need to carry out a few simple procedures in the right order. We are going to create a certificate signing request, use it to generate our SSL certificate, verify the domain name and install the SSL certificate in CloudFront. All of these steps require just a browser and the AWS command line client.

Generate a Certificate Signing Request

Even though you might be tempted to skip this step and use the automated CSR generator which is part of the certificate issuance process, please don't. You'll need the domain key you'll generate as part of the CSR below to refresh the Let's Encrypt SSL certificate every 3 months.

  • Go to ZeroSSL. That's a free service which let you create Let's Encrypt SSL certificates which we're going to be using throughout this guide.
  • On the left hand side click on Certificates and Tools.
  • Scroll down, even though there seems to be nothing there. You'll now see a section appearing, called CSR Generator. Click on the Start button below it.
  • In the Domains area enter the custom (sub)domain name for your CloudFront distribution. For example, cdn.example.com but NOT www.example.com or just example.com (these last two examples are WRONG).
  • To its right, select 2048 bits. This is very important. Amazon will NOT work with 4096 bits keys.
  • Fill in the information under Organization through Country. Although these are optional they will be part of the certificate and visible to your clients. It's a good idea entering your real information here.
  • Now click on the Generate button.
  • After a short while the two text areas are filled with some data. We'll need to copy them to some files. Open up a plain text editor (e.g. Notepad, GEdit, TextWrangler, Smultron and so on).
  • Save the left text area's content into a file called domain-key.txt
  • Save the right text area's content into a file called csr.txt.

Issuing the certificate

Issuing the certificate consists of three sub-steps which must be carried out in the order stated below. Please review these steps because you can't halt the process and resume it later. If you find out you're missing something you'll have to redo this whole step.

Run the certificate wizard

  • Go back to ZeroSSL.
  • On the left hand side click on Certificates and Tools.
  • Find the FREE SSL Certificate Wizard section. Click on the Start button below it.
  • Enter your Email address.
  • DO NOT fill in the domain name. We will use our CSR instead.
  • Remember the two text files you saved above? Open them with a plain text editor (e.g. Notepad, GEdit, TextWrangler, Smultron and so on).
  • Paste the content of the file called domain-key.txt to the left text area.
  • Paste the content of the file called csr.txt to the right text area.

Now you have to make a very important choice: how to verify your CloudFront distribution's (sub)domain. I prefer to use the DNS method which involves creating a TXT record in the DNS zone. This method, however, only works well if your DNS propagates fast. If you are using your host's DNS servers you'll need to use the HTTP verification method instead which has you upload a file to your CDN. Now that you made your choice...

  • ...select your verification method.
  • Check the two boxes to accept the ZeroSSL terms of service and Let's Encrypt service agreement.
  • At the top right of the page click on Next to proceed to the next step.

Save your Let's Encrypt account key

At this point ZeroSSL has created an account key for you. You will need it in the future to refresh your certificate.

Click on the download button to download it to your computer. Name the file account-key.txt.

Warning: do not confuse this file with the domain-key.txt file. They are different files with a different purpose!

After making sure the file is safely on your computer click on the Next button.

Verify your domain name

Depending in the verification method you chose, you will have to verify your site either through DNS or through HTTP (file).

The instructions are on your screen. If you are using the domain verification method you need to create a DNS TXT record containing the information presented on the screen.

If you chose the HTTP verification method instead you're asked to create folder .well-known in your distribution and upload a file into that folder with the content specified on your screen.

In both cases the content you see on your screen is a random, impossible to guess token. Since you are able to place this impossible to guess thing somewhere on the domain you claim you own it means that you are really in control of the domain, therefore you have the right to create SSL certificates for it.

Once you have placed the TXT record in your DNS or the file in your distribution (and you're sure it's been propagated) click Next to complete the verification process. This is usually the most nerve-wracking part of the process since you are essentially in a hurry up and wait mode until the DNS / file propagates so that ZeroSSL can see it.

Create and save your SSL certificate

If you're here, you've got your shiny new SSL certificate on your browser. We need to do a bit of work before we can use them, though.

  • Open up a plain text editor (e.g. Notepad, GEdit, TextWrangler, Smultron and so on).
  • Make sure that the left hand text area's content is identical to your domain-key.txt file's contents. Technically speaking this is your certificate's private key which was generated as part of the CSR issuance at the very beginning of this guide.
  • From the right hand text area's content, copy the text between the FIRST set of -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- headers, including these headers, into a file called domain-crt.txt. This is the SSL certificate for your domain.
  • From the right hand text area's content, copy the text between the SECOND set of -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- headers, including these headers, into a file called issuer-crt.txt. This is the SSL certificate of the issuing Certificate Authority (in our case it's Let's Encrypt).

As you observed we had to split the file in the right hand text area into two separate files. That's because the AWS CLI client expects the two SSL certificates to be presented separately. ZeroSSL provides them in a single file because, unlike AWS, most web servers serving regular HTTPS sites expect them to be presented in the same file.

Sanity check

If you have followed this guide to the letter you now have the following files in the same directory on your computer:

  • account-key.txt. The ZeroSSL and Let's Encrypt account key you will need to refresh the certificates in the future.
  • domain-key.txt. The private key for the SSL certificates issued for the CloudFront distribution's (sub)domain.
  • csr.txt. The Certificate Signing Request for the CloudFront distribution's (sub)domain.
  • domain-crt.txt. The actual SSL certificate for the CloudFront distribution's (sub)domain.
  • issuer-crt.txt. The SSL certificate of the Certificate Authority (Let's Encrypt) which issued the certificate above.

If you're missing any file you won't be able to install the certificate. Check what you're missing and follow these instructions again.

Install the certificate on Amazon Web Services

I never figured out how to use the AWS web interface to install the certificate. Therefore I'm using the AWS CLI (command line) client instead.

Open a terminal and change into the directory where you saved the text files above. Run the following command, replacing CHANGE_ME with a unique identifier. I recommend using the convention domain_year_month_date, e.g. cdn_examplecom-2017-01-17.

aws iam upload-server-certificate \
--server-certificate-name CHANGE_ME \
--certificate-body file://domain-crt.txt \
--private-key file://private-key.pem \
--certificate-chain file://issuer-crt.txt --path /cloudfront/

If you are on Windows please remove the forward slashes (\) and new lines from the command above and run it as one, big line, e.g.

aws iam upload-server-certificate --server-certificate-name CHANGE_ME --certificate-body file://domain-crt.txt
	--private-key file://private-key.pem --certificate-chain file://issuer-crt.txt --path /cloudfront/

Use the certificate in CloudFront

  • Log into AWS Console
  • Go to CloudFront, Distributions
  • Find the distribution that corresponds to the SSL certificate record you created and click on it.
  • In the General tab click on the Edit button
  • Scroll down to Custom SSL Certificate (example.com).
  • Choose the new certificate (e.g. cdn_examplecom-2017-01-17) from the drop-down.
  • Scroll all the way down, click on Yes, Edit

Remember that Let's Encrypt SSL certificates have a short life time (three months) by design. You will need to replace them before they expire, otherwise your HTTPS distribution will be broken. It's a great idea setting a recurring alarm every 12 weeks to renew your certificates. If you're counting you'll see that's shorter than the 90 days given to you by Let's Encrypt. That's on purpose. You might be traveling, sick or otherwise unavailable to renew the certificate on the date. It's best to give yourself some leeway. About a week should be fine unless you plan on dropping off the face of the earth for several weeks at a time.

Refreshing the certificate

So, your certificate is about to expire. You need to refresh it, i.e. replace it with a new one. Thankfuly this process is much simpler and will take you about 5' to 10'.

First we need to get a new certificate. We will need the contents of some of the text files we created above. Remember when I told you they are important? That's why! The process is similar to issuing a certificate, with a couple of signifficant changes. Instead of providing the domain secret key you will be providing your ZeroSSL / Let's Encrypt account key. Morever you will have to use the same domain verification method as before. I am clearly marking the steps you need to watch out below.

  • Go to ZeroSSL.
  • On the left hand side click on Certificates and Tools.
  • Find the FREE SSL Certificate Wizard section. Click on the Start button below it.
  • DO NOT fill in the domain name. We will use our CSR instead.
  • Open up a plain text editor (e.g. Notepad, GEdit, TextWrangler, Smultron and so on).
  • Watch out! Paste the content of the file called account-key.txt to the left text area.
  • Paste the content of the file called csr.txt to the right text area.
  • Watch out! Select the same verification method you had chosen before
  • Check the two boxes to accept the ZeroSSL terms of service and Let's Encrypt service agreement.
  • At the top right of the page click on Next to proceed to the next step.
  • From the right hand text area's content, copy the text between the FIRST set of -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- headers, including these headers, into a file called domain-crt.txt. This is the SSL certificate for your domain.
  • From the right hand text area's content, copy the text between the SECOND set of -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- headers, including these headers, into a file called issuer-crt.txt. This is the SSL certificate of the issuing Certificate Authority (in our case it's Let's Encrypt).

You might wonder why save the issuer-crt.txt file again since we already have it. According to Let's Encrypt's roadmap they will use a different Certificate Authority (CA) certificate to issue SSL certificates at some point. In essence, they will transition from having their CA certificate signed from a third party root CA to being a root CA themselves. Therefore the issuer certificate may change at any point. Better be safe than sorry!

Finally, you need to carry the steps in Install the certificate on Amazon Web Services and Use the certificate in CloudFront again. It is very important noting that you cannot use the same unique identifier for the new SSL certificate. You will need to use a different one (hence calling it a unique identifier). That's the reason I told you to use a naming convention which includes the domain name and date as this is a combination that's pretty much guaranteed to be unique on your account.

Acknowledgements

Let's Encrypt is a trademark of the Internet Security Research Group. All rights reserved.

Many thanks to Phil Taylor, George Wilson and Michael Babker for the discussions we've had in several occasions about the integrity and security of Joomla! core and extension updates. These discussions prompted me to take action last year, eventually leading to me writing this blog post. Now if we could only convince people to use digital signing of extension packages...

This article wouldn't have been possible but for the existence of a Panda Strike article on this subject enriched with information from the Using HTTPS with CloudFront AWS documentation page and the ZeroSSL documentation. I believe I have provided some added value to the original article by clarifying some non-obvious spots and providing working instructions for the AWS CLI and AWS console, therefore replacing what doesn't work (for me?).

All of these instructions were tested on Akeeba Backup's own CloudFront distribution. The incentive was to ensure the integrity of downloads of our free of charge software and, most importantly, the integrity of the update information stream for our software used by hundreds of thousands of sites around the world. We're already at the third set of Let's Encrypt SSL certificates and I can attest that it really works. Sorry for not writing this post sooner, I had to make sure that all issues had been ironed out before telling you how to follow my steps.

12 comments

  • Getting the error when generating SSL on zerossl:

    Error information: Error creating new cert :: certificate public key must be different than account key.
    • You are trying to use the wrong file. Look at the error message. It tells you as much. If you are not sure what this means, consult ZeroSSL's documentation on their site.
    • The interface must have changed since this tutorial. At the moment of writing, this (great) tutorial needs to be updated.
      The left side is now dedicated to the account-key, which is not (and should not be) the same as the domain-key.
      You just need to leave the left section blank. By clicking on Next,  ZeroSSL will generate you an account-key, which you need to download (you will need it later, as mentioned in this tutorial).
      Click again on Next to go the verification step.

      You should be fine.
      • Forget my intial reply.
        The tutorial explains it properly, i just got confused reading it and run into the same problem.

        My bad ! :(
  • Hi
    I wanted to setup AWS EC2 + Cloudfront + Let's Encrypt. Wanted to have both EC2 instance and cloudfront work with cert provided by Let's Encrypt. Any idea how to go about doing that?
    • Create a different certificate for each instance. LE certificates are issued for a specific domain. Since the EC2 instance and the CDN are different domains you need a different certificate for each one.
  • Thnx for the prompt reply. If I understand you correctly, I have to create 2 different certs, one for example.com and another for cdn.example.com from Let's Encrypt. We can use Zero SSL for both of them? The tutorial BTW is gr8 and very detailed.
    • Correct, you need two different certificates. Yes, you can use ZeroSSL for both of them.

      Thank you for your kind words :)
  • Hi, Why do you say mentioning 'www.example.com' and 'example.com' for the domain names is wrong? That part is not quite clear.
    • TL;DR: The fully qualified subdomain on the SSL certificate must match exactly the fully qualified subdomain you are securing.

      In my example, the resource you are securing with SSL is cdn.example.com. That's the fully qualified subdomain (subdomain.domain.tld) of your CloudFront distribution. The other domain names, like www.example.com and example.com, are not the CDN. You cannot use the same SSL certificate on two different subdomains unless the certificate was issued against both subdomains. This is something Let's Encrypt does not support as far as I know.