Last update on .

Two months later than planned (as it usually goes with side projects ;), and a first release of Gimmecert, a small utility for issuing X.509 certificates for testing purposes, is finally out.

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:

gimmecert status

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

    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

    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

    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:

├── 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.

Happy hacking!


Pingbacks are closed.


Comments are closed.