Easy Self-Signed Certificates for ASPNET Core with TLS-Gen

Gian Lorenzetto, PhD
4 min readJan 13, 2021

--

Photo by Lewis Keegan on Unsplash

I ran into an issue setting up a TLS connection between an ASPNET Core API and a RabbitMQ service running in a Docker container (Ubuntu based). Although the instructions in the Rabbit TLS Support doc are very good, I wanted to document the end-to-end process. Although the individual steps are straight forward, there are quite a lot of them and one misstep along the way leads to a broken connection and plenty of wasted time trying to work out what went wrong.

The below is useful for testing in local development environments, but needless to say, take the usual precautions in shared and production environments to ensure the security of your connections.

[NOTE This is an older article that I am reposting in the event it’s useful to others — and myself when I need to reuse tls-gen! Drop any corrections in the comments.]

[NOTE 2 — a few people have asked about the certificate generation. I choose to use tls-gen since that is what the RabbitMQ doc uses, but any valid certificates in the right format will work. For an alternative, check out the step cli (thanks Anthony Attwood for the link!!)]

To create a new set of certificates, the tl;dr is -

  1. Use tls-gen to create new certs.
  2. Convert CA root certificate to .crt (needed for Ubuntu clients).
  3. Select / convert to the appropriate certificate type using openssl.
  4. Update RabbitMQ configuration to enable TLS and specify the location of the certificates.
  5. Update clients (API’s) with latest client certificate.

With that out of the way, let’s get into the details!

Creating TLS Certificates

The first step is to use the tls-gen tools to create a self-signed certificate. By default the local hostname is used as the Common Name (CN), for non-local environments you can specify the required hostname using the CN parameter.

The steps to create a certificate are:

  1. Clone the tls-gen GitHub repo.
  2. Change directory to the tls-gen/basic folder (basic creates a simple CA root and leaf certificate, for more complicated certifcate chains, see the tls-gen doc).
  3. Create a set of certificates. You will need to set PASSWORD=<YOUR_STRONG_PASSWORD>. For use on a remote server you must also set CN=<HOSTNAME>

This creates a set of client, server and CA root certificates in .pem and .p12 formats.

Note — If running RabbitMQ via Docker services, you can use the service name as the <HOSTNAME> parameter value above.

Selecting the Right Certificate Type

If RabbitMQ is running in a *nix based container OS, use the .pem files directly. For ASPNET Core applications running on Ubuntu, the CA root certificate needs to be converted to the .crt format using OpenSSL as follows.

> openssl x509 -in ca_certificate.pem -inform PEM -out ca_certificate.crt

For windows, the .pem files can be converted to .cer using -

> openssl x509 -outform der -in server_certificate.pem -out server_certificate.cer

Install Certificates into RabbitMQ

In order to use TLS, add the following to the RabbitMQ configuration file (…infastructure/rabbitmq/config/rabbitmq.conf) -

listeners.ssl.default = 5671ssl_options.cacertfile = /path/to/certs/ca_certificate.pem
ssl_options.certfile = /path/to/certs/server_certificate.pem
ssl_options.keyfile = /path/to/certs/server_key.pem
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = false

This tells RabbitMQ to use TLS on port 5761 with the given certificates, performing peer verification with the client when connecting.

Note — Ensure the Docker container publishes the TLS port (5761by default).

Install Certificates into the API

ASPNET Core APIs need to reference the client .p12 certificate in order to establish a connection to the RabbitMQ host.

Add the following configuration to the RabbitMQ section of relevant appsettings.json files, replacing

  • RABBITMQ_HOSTNAME with the hostname (aka service name) of the RabbitMQ container.
  • /PATH/TO/CERTS with the path to where the client certificate (.p12) is accessible from the API container.
  • YOUR_STRONG_PASSWORD with the password provided to the tls-gen tool when creating the certificates.
“RabbitMq”: {
“BrokerUri”: “amqp://guest:guest@<RABBITMQ_HOSTNAME>:5671/“,
“Ssl”: {
“SslServerName”: “<RABBITMQ_HOSTNAME>”,
“SslClientCertPath”: “/PATH/TO/CERTS/client_key.p12”,
“SslClientCertPassphrase”: “<YOUR_STRONG_PASSWORD>
}
}

In the API, add the following to the RabbitMQ factory setup to enable (or disable) TLS support -

private void CreateRabbitMqConnection(RabbitMqConfiguration config)
{


var factory = new ConnectionFactory {Uri = new Uri(config.BrokerUri), AutomaticRecoveryEnabled = true};
if (config.Ssl != null)
{
factory.Ssl.ServerName = config.Ssl.ServerName;
factory.Ssl.CertPath = config.Ssl.ClientCertPath;
factory.Ssl.CertPassphrase = config.Ssl.ClientCertPassphrase;
factory.Ssl.Enabled = true;
}
else
{
factory.Ssl.Enabled = false;
}
}

ASPNET Core API Docker Containers

The ASPNET Core API’s use peer validation to establish the TLS connection, which requires the CA root certificate used to sign the ( .p12) to be trusted. In the case of the tls-gen approach above, it produces a self-signed certificate, so the CA root must be installed into the trusted certificate store on the client.

In order to do this on Ubuntu -

  1. Copy the ca_certificate.crt file into the folder /usr/local/share/ca-certificates/
  2. Run the CA root certificate update utility update-ca-certificates

This can be achieved from the Dockerfile by including the following -

COPY ./local/path/to/certs/ca_certificate.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates

Conclusion

A lot of steps, but quite straight forward once you’ve done it a couple of times. Note that the TLS-gen and OpenSSL are extremely useful tools to know when you need certificates for local dev and testing.

--

--