Using 'cert' and 'key' with Erlang/OTP 'ssl'

2021-02-26 16:16:00 +0000 erlang

Ordinarily when writing an SSL/TLS server or client using Erlang/OTP, you’ll use the certfile and keyfile options, as follows:

Port = 5555,
LOpts = [{certfile, "server.crt"}, {keyfile, "server.key"}],
{ok, LSock} = ssl:listen(Port, LOpts).

The ‘ssl’ documentation also shows the cert and key options, which allow you to specify the certificate and key directly, rather than needing to specify filenames.

common_option() =
    % ...
    {cert, cert() | [cert()]} |
    % ...
    {key, key()} |
    % ...

cert() = public_key:der_encoded()
key() = {'RSAPrivateKey' | ...,

Note that the ability to specify multiple certificates (i.e. a complete certificate chain) was added in ssl-10.2, in OTP-23.2. My system-installed Erlang/OTP version is 23.0.3, so I wasted some time being confused by that.

I probably shouldn’t have been confused, since it was likely my request to the Erlang mailing list that resulted in the feature being added. Thanks, Ingela!.

Anyhow, if – for whatever reason – you want to load the certificate and key from files and pass them directly, you need to do something like this:

load_certificates(CertFile) ->
    {ok, PemBin} = file:read_file(CertFile),
    Entries = public_key:pem_decode(PemBin),
    [Der || {'Certificate', Der, not_encrypted} <- Entries].

load_key(KeyFile) ->
    {ok, PemBin} = file:read_file(KeyFile),
    [{Type, Der, not_encrypted} | _] = public_key:pem_decode(PemBin),
    {Type, Der}.

listen(Port, Opts0) ->
    Opts = lists:map(
        fun({certfile, CertFile}) -> {cert, load_certificates(CertFile)};
            ({keyfile, KeyFile})) -> {key, load_key(KeyFile)};
            (Opt) -> Opt
        end, Opts0),
    ssl:listen(Port, Opts).

Of interest here:

Would you actually do this in real code? Probably not. The only real reason you might do this is to cache the certificate and key, to avoid going back to disk. You’re probably not going to actually need to do that, because: