Generating good modern self-signed TLS certificates in today's world
Once upon a time, generating decently good self-signed certificates
for a host with OpenSSL was reasonably straightforward, especially if you didn't know about
some relevant nominal standards. The certificate's Subject name
field is a standard field with standard components, so OpenSSL would
prompt you for all of them, including the Common Name (CN) that
you'd put the hostname in. Then things changed and in modern
TLS, you really want to put the hostname in the Subject Alternative
Name field. SubjectAltName is
an extension, and because it's an extension '
openssl req' will not
prompt you to fill it in.
(The other thing is that you need to remember to specify
as one of the arguments; otherwise '
openssl req' will use SHA1
and various things will be unhappy with your certificate. Not all
examples you can find on the Internet use '
-sha256', so watch
You can get '
openssl req' to create a self-signed certificate
with a SAN, but since OpenSSL won't prompt for this you must use
an OpenSSL configuration file to specify everything about the
certificate, including the hostname(s). This is somewhat intricate, even if it turns out to be
possibly to do this more or less through the command line with
suitably complicated incantations.
I particularly admire the use of the shell's usually obscure
Given how painful this is, what we really need is a better tool to create self-signed certificates and fortunately for me, it turns out that there is just what I need sitting around in the Go source code as generate_cert.go. Grab this file, copy it to a directory, then:
$ go build generate_cert.go $ ./generate_cert --host www.example.com --duration 17520h 2017/04/11 23:51:21 written cert.pem 2017/04/11 23:51:21 written key.pem
This generates exactly the sort of modern self-signed certificate that I want; it uses SHA256, it has a 2048-bit RSA key (by default), and it's got SubjectAltName(s). You can use it to generate ECDSA based certificates if you're feeling bold.
Note that this generates a certificate without a CN. Since there are real CN-less certificates out there in the wild issued by real Certificate Authorities (including the one for this site), not having a CN should work fine with web browsers and most software, but you may run into some software that is unhappy with this. If so, it's only a small modification to add a CN value.
(You could make a rather more elaborate version of generate_cert.go with various additional useful options, and perhaps someone has already done so. I have so far resisted the temptation to start changing it myself.)
A rather more elaborate but more complete looking alternative is Cloudflare's CFSSL toolkit. CFSSL can generate self-signed certificates, good modern CSRs, and sign certificates with your own private CA certificate, which covers everything I can think of. But it has the drawback that you need to feed it JSON (even if you generate the JSON on the fly) and then turn its JSON output into regular .pem files with one of its included programs.
For basic, more or less junk self-signed certificates, generate_cert is the simple way to go. For instance my sinkhole SMTP server now uses one of these certs; SMTP senders don't care about details like good O values in your SMTP certificates, and even if they did in general spammers probably don't. If I was generating more proper self-signed certificates, one where people might see them in a browser or something, I would probably use CFSSL.
(Although if I only needed certificates with a constant Subject name, the lazy way to go would be to hardcode everything in a version of generate_cert and then just crank out a mass of self-signed certificates without having to deal with JSON.)
PS: We might someday want self-signed certificates with relatively proper O values and so on, for purely internal hosts that live in our own internal DNS zones. Updated TLS certificates for IPMI web interfaces are one potential case that comes to mind.
PPS: It's entirely possible that there's a better command line tool for this out there that I haven't stumbled over yet. Certainly this feels like a wheel that people must have reinvented several times; I almost started writing something myself before finding generate_cert.