Tumbling down the crypto rabbit hole — Part I

matt volante
arpcache
Published in
5 min readFeb 15, 2018

I wasn’t going to name this series of blogposts “tumbling down the crypto rabbit hole”…because increasingly the word “crypto” has been used to define “cryptocurrency”…much to the dismay of many in the infosec community such as Sarah Jeong and Isis Lovecruft. While I don’t disagree with the assertion that “cryptocurrency” is not “crypto”, I try not to use energy to fight against the evolution of language however much it may make me cringe. But yeah, stop calling bitcoin and bitcoin-wannabes “crypto”.

I’m curious about cryptography. I read about it, I use toolsets that implement cryptography such as openssl and PyNaCl. But at it’s essence, cryptography is math. I’m not a math major nor does my life lend itself to spending large amounts of time groking the math behind modern-day cryptography. But I am fascinated by this subject, and I try to heed the words of those whom I have discovered to be experts in the field.

Alright, with that disclaimer out of the way, I’ll get to what I wanted to write about. Oftentimes I try to figure out how to do something “the right way” so I can learn how it works. This time around I wanted to see if I could create a best-practice-secure certificate that can sign other certificates…kind of like rolling my own Root CA.

A while ago a friend was running into browser warnings in a demo IIS environment that he set up to show clients some new functionality that his web service was working on. He didn’t want the clients to see warnings but needed to host the demo on an ip address instead of a domain name. And he needed to run this on a local IIS install on his laptop. As Chrome is now doing great things with browser UI, it’s harder to get that shiny, green “Secure” label on your website. Well, not harder, but Chrome is definitely picky about what it deems is a secure certificate — and rightly so.

I did a bit of research and basically came up with these steps:

1. Generate your own CA key:

hank@ubuntu01:~$ sudo openssl genrsa -out hankrootCA.key 2048
Generating RSA private key, 2048 bit long modulus
..+++
………+++
e is 65537 (0x10001)
hank@ubuntu01:~$

2. Generate your own CA cert using the key:


hank@ubuntu01:~$ sudo openssl req -x509 -new -nodes -key hankrootCA.key -sha256 -days 1024 -out hankrootCA.pem


3. Take the resulting .pem file and import it into the computer running your browser under “Trusted Root Certificates” Store. I loaded MMC and the certifcate snap-in for “Computer”. The import process doesn’t expect a .pem file, but import it anyway it’ll work.


4. Generate key for your website:


hank@ubuntu01:~$ sudo openssl genrsa -out hank.key 2048
Generating RSA private key, 2048 bit long modulus
…………………………………………………………………………………………………….+++
…………+++
e is 65537 (0x10001)
hank@ubuntu01:~$

5. Create your site CSR — be sure to use your IP address as Common Name:


hank@ubuntu01:~$ sudo openssl req -new -key hank.key -out hank.csr

6. Sign the CSR with your CA key:

hank@ubuntu01:~$ sudo openssl x509 -req -in hank.csr -CA hankrootCA.pem -CAkey hankrootCA.key -CAcreateserial -out hank.crt -days 500 -sha256

7. Finally, generate the .pfx file that IIS needs:

hank@ubuntu01:~$ sudo openssl pkcs12 -export -out hank.pfx -inkey hank.key -in hank.crt
Enter Export Password:
Verifying — Enter Export Password:
hank@ubuntu01:~$

8. Take the .pfx and import it into IIS. Switch your site bindings to use the cert et voila.


Alright, so that was alot of steps but it solved the problem my friend had at the time. I’m sure there are other ways to tackle this with openssl using less steps or more optimized commands. But I learned a bit about public key cryptography and openssl while going through this exercise.

So this seems fine. But something nagged at me. It’s using the RSA algorithm. While *probably* fine, there are lots of rumblings that RSA isn’t good enough any longer and Elliptic Curve algorithms are the way to move forward. That being said, RSA, when implemented properly, is still considered “safe”…but is also still considered susceptible to side-channel attacks and weaknesses in certain RNG operations.

Ok so this will be easy, right? I want to generate a key using an EC algorithm instead of RSA. Here’s the openssl command to do just that:

openssl ecparam -genkey -out hankrootCA.key -name prime256v1

In the above example I’m choosing the NIST P-256 curve. This is considered the one that “works with most browsers”. Already I’m not liking this. First off, it’s NIST (NSA). And secondly, it’s NIST (NSA).

Ok so I don’t want to use a NIST curve. I’d rather use the default curve that OpenSSH is now using…which is Curve25519. This was not developed by NIST, instead by Daniel J. Bernstein, the same computer scientist that brought us qmail and djbdns way back in the day. Curve25519 is considered to be less prone to implementation errors.

To see a list of curves supported by openssl, run this command:

openssl ecparam -list_curves

This will spit out a long list of curves available. When I first ran this, I didn’t see Curve25519 in the list. Turns out that’s because I was using OpenSSL 1.0.2g (default Ubuntu 16.0.4 release), and 25519 support didn’t get added until version 1.1.

Alright, so I downloaded and built OpenSSL 1.1.0g from source. This went smoothly, until I tried to run openssl to verify the version:

/usr/local/bin/openssl: error while loading shared libraries: libssl.so.1.1: cannot open shared object file: No such file or directory

What? Why didn’t the openssl libraries get installed? Surely ‘make’ would have informed me of this error during compile time…well turns out they were installed but only into /usr/local/lib and the binary expected them to be in /usr/lib. The library locations were not being linked properly. This was identified by the OpenSSL project. Fine. I fixed that little issue.

Alright so now I have the correct version of openssl, so now I can make my Curve25519 cert! Right?

uh. Not so much. -list_curves was still not showing me 25519 anywhere. This is because Curve25519 is treated as a distinct algorithm, for reasons. So the EC API in openssl doesn’t list it. BUT…I can still generate an X25519 key like this:

openssl genpkey -algorithm X25519 -out hankrootCA.key

Great! Now let’s use this fancy X25519 key to sign our “root” cert:

openssl req -x509 -new -nodes -key hankrootCA.key -sha256 -days 1024 -out hankrootCA.pem

Errr…oops..this happens:

3072886528:error:0608D096:digital envelope routines:EVP_PKEY_sign_init:operation not supported for this keytype:crypto/evp/pmeth_fn.c:40:


Another roadblock. What’s going on here? Well crap, X25519 cannot be used to sign certs. Only for key generation/exchange/agreement.

So what’s this mean? This means if you want to create your own X.509 self-signed certificate you have to either use RSA (has implementation problems) or EC with the built-in openssl algorithms, and you can’t use a Curve25519 key to sign your cert. This doesn’t mean signing keys for all X.509 certs are bad because of this…the RSA algorithm and NIST curves can be perfectly safe. In fact, that’s all openssl supports now. I’m sure there’s tons more to this story. For another day.

This odyssey is what I mean by falling down the rabbit hole of crypto. Cryptography is a massively complex topic, and implementing cryptography properly is a rare unicorn indeed. I happen to find all of this fascinating so I don’t mind jumping through the odd hoop so I can learn something new and strive to do things the right way with the best security tools available.

--

--

matt volante
arpcache
Editor for

I write about networks, protocols, infosec and esoteric technology scraps that i’ve found useful in my 20 year technology career.