Get free SSL Certificates with Perl

NB: This project can be found on both CPAN and GitHub. Check the GitHub page for more details regarding the installation, usage and external modules integration. This post was made back in 2016, and some information in it might be outdated, but the GitHub information is always up to date.

This week I noticed that one of the SSL certificates I previously paid for is expiring soon. While I could of course buy it for another year, where's the challenge in that? Plus, there is now Let's Encrypt - a new Certificate Authority providing absolutely free certificates, which is in Public Beta at the moment.

While you could use an official Let's Encrypt client, it requires root on the server, so it's not quite an option. There are some viable alternatives in Python for example, but surprisingly I found that Perl is lacking a convenient LE client. "Lo and behold", enters Crypt::LE.

Now, the library itself has a rather straightforward usage. Additionally, a boilerplate module is provided to create "challenge handlers" - Crypt::LE::Challenge::Simple. So far, so good. However, even though this module has been created in a rather short time span, I wanted it not only to work, but also to be easy to use for those who just wants certificates, without writing any code. As a result, the module is shipped with a Let's Encrypt client -

First, you will need to install Crypt::LE. You can do it with "perl -MCPAN -eshell" if you prefer it that way, by giving a command to "install Crypt::LE", you could also do it with just "cpan -i Crypt::LE", or you could do it with "cpanm Crypt::LE" if you tend to use CPANminus. Personally I prefer CPANminus since the days quite a few years ago when I had some troubles using cpan for installations on set of machines with very limited resources. It's also available as a package for many Linux distributions and can be installed for example in Debian/Ubuntu with "apt-get install cpanminus".

Run cpanm Crypt::LE or cpan -i Crypt::LE

That should install both the library and "" client. If you now try running without any parameters, you will see a help screen. Typical usage line will be like this: --key account.key --csr domain.csr --csr-key domain.key --crt domain.crt --domains "www.domain.ext,domain.ext" --generate-missing

Now imagine that you are running this for the very first time and you don't have any account with Let's Encrypt. What will happen first is that your account key will be generated and saved as 'account.key' file (keep it private), then another key (this time for the Certificate Signing Request) will be generated, and finally a CSR itself will be generated for the domains you have specified. Next time you run exactly the same command line, your account key and CSR will be loaded, without generating them again.

Once the key and CSR have been successfully loaded or generated, LE client will proceed with the work necessary to register an account (if it's not registered already) and then validate domains and request your certificate. During the validation, you will see messages like the following for each domain you have specified:

Challenge for www.domain.ext requires:
A file 'XXXXXXXXXXXXXXXX' in '/.well-known/acme-challenge/' with the text: XXXXXXXXXXXXXXXX.YYYYYYYYYYYYYYYYYYYYYYYY

Those are the files Let's Encrypt expects to request and find, to verify that the domains in question are yours. Put the file in place and press Enter.

If the domains were successfully verified, your certificate will be retrieved and saved. And all that with just one command! You should then be able to use the certificate with your server. For example for Nginx it will be like this:

ssl_certificate ssl/domain.crt;
ssl_certificate_key ssl/domain.key;

With the properly configured SSL settings and enabled "Strict Transport Security" (HSTS), you should be then getting an A+ on Qualys SSL Server Test. To add HSTS you can use:

add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";

LE client has other options you might find useful. For example:

  • --generate-only - Will make it exit once the missing keys and CSR are generated.
  • --verified - Will skip verification for already verified domains.
  • --path - Will be creating required challenge files for you in the given path.
  • --handle-with - Use some external module to handle challenges with.
  • --handle-as - Choose the challenge type (http/dns/tls/...).
  • --complete-with - Use some external module to handle process completion with (more about that below).

However, the most important option is --live. Without it, all the work will be done against the staging server, rather than live server, so none of the certificates you are getting will be "real" (trusted). This is done because live Let's Encrypt servers have some limits on registrations from the same IP or getting more than certain amount of certificates per domain per week. You can find more details in the documentation. So always add this option to get a trusted certificate.

You can easily extend the functionality of the client by creating some new modules, similar to the provided Crypt::LE::Challenge::Simple and then using "--handle-with" and "--handle-as" options. You can handle other than 'http' types of challenges with such new modules, or make challenge handling fully automated (copy challenge files to proper places or to the remote hosts, etc.). You can also pass custom parameters to those external modules in a form of valid JSON documents: ... --handle-with Crypt::LE::Challenge::Simple --handle-params '{"key1": 1, "key2": 2, "key3": "something"}'

Additionally, from version 0.05, you should be able to use 'completion handlers' with Those are similar to challenge handlers, but instead of the challenge data they will be receiving certificate data (including the domain and issuer's certificate, certificate file name and key file name) and also optional parameters (if you provided any): ... --complete-with Crypt::LE::Complete::Simple --complete-params '{"key1": 1, "key2": 2, "key3": "something"}'

With completion handlers you can easily add a process of copying the resulting certificate and key into proper directory or to some other host if you like. Note: the domain certificate and issuer's certificate which are passed to completion handler are not merged (they are merged by when saved though). Keep that in mind while processing the data. See Crypt::LE::Complete::Simple for an example of such handler.

Finally, from version 0.09 you should be able to also handle the domain verification results. For example, if you use --handle-with Crypt::LE::Challenge::Simple option with client application, then for each domain (assuming that you haven't set '--handle-as' parameter to the value different than 'http') accept_challenge() process will call "handle_challenge_http" method first, allowing you to create the required challenge files and do some other work. After that verify_challenge() process will call "handle_verification_http" method, allowing you to remove challenge-related records/files and react on the verification status (which is also passed to the method).