In short, Gimmecert will let you set-up an X.509 CA hierarchy, and use it to issue TLS certificates (server and client ones). Of course, all of that can be achieved using OpenSSL or GnuTLS CLI, but the amount of files/commands I had to run in cases where I just needed to quickly get a couple of certificates for testing drove me nuts.
It's a Python tool, and works with Python 3.4, 3.5, and 3.6 (at time of this writing). So, no Python 2.7 support (yay? ;).
WARNING: When I mean that the tool is supposed to be used for tests only, I mean it. Please don't use it in production :)
Let's have a quick example on how to use Gimmecert.
Start off with creating a directory for our hypothetical project:
mkdir -p ~/projects/myproject
Since that's the easiest way to install it, let's use the virtualenvwrapper:
mkvirtualenv -p python3 myproject workon myproject pip install gimmecert
With the Gimmecert tool now installed and available, it's time to set-up the CA hierarchy. This is as simple as:
cd ~/projects/myproject/ gimmecert init
The tool provides nice informative output after running commands so you don't have to keep guessing where files have been placed etc:
CA hierarchy initialised. Generated artefacts: CA Level 1 private key: .gimmecert/ca/level1.key.pem CA Level 1 certificate: .gimmecert/ca/level1.cert.pem Full certificate chain: .gimmecert/ca/chain-full.cert.pem
As you can see, a local sub-directory called .gimmecert is used for storing all the X.509 artifacts. The tool has created a 1-level deep (the default) CA hierarchy.
With CA hierarchy in place, let's issue some server certificates:
# Server certificate with DNS subject alternative name equal to its # "entity name". gimmecert server myserver1.local # Server certificate with one extra DNS subject alternative name (in # addition to the "entity name"). gimmecert server myserver2.local myservice.local
The output from these two would be:
# First command output. Server certificate issued. Server private key: .gimmecert/server/myserver1.local.key.pem Server certificate: .gimmecert/server/myserver1.local.cert.pem # Second command output. Server certificate issued. Server private key: .gimmecert/server/myserver2.local.key.pem Server certificate: .gimmecert/server/myserver2.local.cert.pem
Server certificates are put into their own sub-directory, as can be seen from the output.
Let's issue a client certificate as well:
gimmecert client myclient1
The output is:
Client certificate issued. Client private key: .gimmecert/client/myclient1.key.pem Client certificate: .gimmecert/client/myclient1.cert.pem
Client certificates are put into their own sub-directory, keeping them nice and separate from the server ones.
If you want to see more information what got created and with what content, there is a convenient status command:
In our particular case, the output from it would be:
------------ CA hierarchy ------------ CN=myproject Level 1 CA [END ENTITY ISSUING CA] Validity: 2018-05-16 21:02:32 UTC - 2019-05-16 21:17:32 UTC Certificate: .gimmecert/ca/level1.cert.pem Full certificate chain: .gimmecert/ca/chain-full.cert.pem ------------------- Server certificates ------------------- CN=myserver1.local Validity: 2018-05-16 21:04:37 UTC - 2019-05-16 21:17:32 UTC DNS: myserver1.local Private key: .gimmecert/server/myserver1.local.key.pem Certificate: .gimmecert/server/myserver1.local.cert.pem CN=myserver2.local Validity: 2018-05-16 21:04:41 UTC - 2019-05-16 21:17:32 UTC DNS: myserver2.local, myservice.local Private key: .gimmecert/server/myserver2.local.key.pem Certificate: .gimmecert/server/myserver2.local.cert.pem ------------------- Client certificates ------------------- CN=myclient1 Validity: 2018-05-16 21:05:41 UTC - 2019-05-16 21:17:32 UTC Private key: .gimmecert/client/myclient1.key.pem Certificate: .gimmecert/client/myclient1.cert.pem
Just to give a quick overview, here is what the file tree with generated artifacts looks like:
.gimmecert/ ├── ca │ ├── chain-full.cert.pem │ ├── level1.cert.pem │ └── level1.key.pem ├── client │ ├── myclient1.cert.pem │ └── myclient1.key.pem └── server ├── myserver1.local.cert.pem ├── myserver1.local.key.pem ├── myserver2.local.cert.pem └── myserver2.local.key.pem
The tool has a couple more features too, but this is the gist of it. If you want to find out more, just have a look at the documentation page.
Now for a bit of dev talk.
There is a couple of things that I really liked while working on this project.
First of all, this is the first project that I have worked upon using test-driven development (TDD), in huge part inspired by the book Test-Driven Development with Python (by Harry J. W. Percival). In light of that, it was particularly interesting to find a good way to test the CLI, both on functional and unit level. Having a 100% code coverage is a very nice consequence as well. Since the project is fairly small, it was also a very nice way to get into TDD itself.
Second (in large part due to TDD again), I kinda like (of course, it is my code after all :P ) how the CLI handling got laid out in the end. Although not perfect in all regards, I like some of the separations and abstractions implemented around it.
Third (and perhaps most importantly), I finally have a tool that has already helped me to ease some of the burden when playing around with TLS. The plan is to start using the tool for testing some of my own Ansible roles down the line (instead of generating the test data/certificates by hand).
To close this off, I should point out that I do plan to follow-up with some more development-related posts, primarily about how to do functional and unit testing for a CLI app (or at least how I've approached it in this project), a little on TDD experiences so far, and also a bit about using argparse with sub-parsers for commands. Most of it is probably nothing revolutionary, but it could perhaps help someone down the line in getting started.