Let's Encrypt on Windows revisited

Note: this post is over a year old, it's very likely completely outdated and should probably not be used as reference any more. You have been warned. :-)

Back in March, I blogged about Let's Encrypt and then I got busy doing other things. This weekend I had another look at the state of things for Windows and discovered that a few industrious people have been building some great things. The on tool I like very much is lets-encrypt-win-simple (lews). This is a small command line application that accepts some parameters to make obtaining a certificate as easy as possible. It supports installing certs into IIS and can set up an automated renewal process as well.

The best part about lews is that I can read the source code and understand what's going on, hurrah for keeping it fairly simple! The second best part was that I could really quickly develop a plugin for it. What I wanted to do is to fully automate obtaining the certificate. Currently it's not yet possible for me to automate the install of the certificate but (at Umbraco) we're working at making that possible as well.

If you've read my previous blog post, you know that Let's Encrypt needs to verify that you own the domain you're trying to purchase a certificate for. Let's Encrypt tells you to put a file in your site with a verification token in it, that they can then read. I used to do this manually but it became tedious, so I wrote a quick ApiController so I can just tell my site: create a file, in folder x with content y. One warning though: this is VERY insecure. I've only secured it with a secret token right now but if you post to this endpoint over http, your request can be intercepted and random files can be written to your website!

Right. A quick test with Postman later and I'm confident that I can now automatically create the verification that Let's Encrypt needs.

Now I need to tell lets-encrypt-win-simple (can we just rename this to Lewis?) how to use this endpoint. Lewis (there, I did it!) allows for plugins to be built, just make a class that inherits from `LetsEncrypt.ACME.Simple.Plugin` and implement the methods you get. I took a peak at their FTP plugin and borrowed some code from that one.

In order to show up in the lews dialog you need to implement a `public override void PrintMenu()` method that tells you something like: "X: press x for my plugin". Then you can handle the keypess in a HandleMenuResponse method that you override and in which you test if "X" has been pressed. That's it, we're in business! We can now do whatever we want.

A bit of code:

Results in an extra menu option:

The prompt here is a bit deceiving, but instead of giving it a host to get a certificate for, you enter one of the menu options.

The code then needs to create a target (basically: a hostname for which to create a certificate) and call the `Auto` method, passing in this target:

    public override void HandleMenuResponse(string response, List targets)
    {
        if (response != "c".ToLowerInvariant()) return;

        var target = new Target
        {
            Host = "cork.nl",
            WebRootPath = string.Empty,
            PluginName = Name
        };

        Auto(target);
    }

Of course you wouldn't have hard coded domains in there, this is just a proof of concept to see if it works.

Then it get's really interesting, Lewis (I always imagine people with the name Lewis have a moustache) will look for a method called `CreateAuthorizationFile` which is where we can call our endpoint with the directory name to create and the contents of the authorization file:

    public override void CreateAuthorizationFile(string answerPath, string fileContents)
    {
        const string token = "not_my_real_secret";
        var endpoint = new Uri("https://cork.nl/umbraco/api/Certificate/ValidationFile/");
        var directoryName = answerPath.Split('/').Last();
        var parameters = string.Format(
            "DirectoryName={0}&FileContent={1}&Token={2}", directoryName, fileContents, token);

        using (var web = new WebClient())
        {
            web.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
            web.UploadString(endpoint, parameters);
        }
    }

 This makes Let's Encrypt happy and they send me a certificate!

Though I am happy with this result, I do wish I could do all this with just a few command line switches so that I don't need to manually tell Lewis (it's sticky, isn't it!) what to do every time I start it. Full automation is on their roadmap and I've sent them a simple pull request to get started. For now, I'll work with my fork which has this logic in it already.

What's not covered in this post, is the `Install` method, in which you can code how exactly to install the certificate on your target server. You can get some inspiration from the built-in plugins for that of course!

All in all, I'm happy that things are moving fast on the Windows side of Let's Encrypt and we'll definitely have a bright and encrypted future ahead of us!

 

Sebastiaan Janssen

Dutch guy living in (and loving) Copenhagen, working at Umbraco HQ. Lifehacker, skeptic, music lover, cyclist, developer.

 

1 comments on this article

Avatar for Rasmus E. Møller Rasmus E. Møller | August 21 2016 20:27
Hi Sebastian,

Fantastic post! Looking forward to the implementation in Umbraco ;-)

Best regards R.