DEV Community

Cover image for Node mTLS from scratch
Sibelius Seraphini for Woovi

Posted on

Node mTLS from scratch

Introduction

Mutual Transport Layer Security (mTLS) is a security protocol that provides encryption, authentication, and data integrity for communications over a computer network, such as the Internet. mTLS is an extension of the Transport Layer Security (TLS) protocol, and it adds the concept of mutual authentication, meaning both the client and the server authenticate each other during the establishment of a secure connection.

It makes sure only the known clients consume server APIs.

You can read the RFC here https://datatracker.ietf.org/doc/html/rfc8705

Woovi is not a payment institution, but we consume some APIs and Webhooks from payment institutions. The Brazil Central Bank requires the usage of mTLS for APIs and Webhooks.

This article explains step by step how to generate all the certificates for both client and server and how to have them set up to provide mTLS.

Generating Certificates

I made this open-source playground node-mtls to easily generate new certificates and validate them in both servers and different HTTP clients.

First, we generate a rootCA

# Generate RootCA
openssl genpkey -algorithm RSA -out rootCA-private-key.pem
openssl req -new -key rootCA-private-key.pem -out rootCA.csr -subj "/CN=root"
openssl x509 -req -days 3650 -in rootCA.csr -signkey rootCA-private-key.pem -out rootCA.crt
Enter fullscreen mode Exit fullscreen mode

rootCA-private-key.pem is the private key
rootCA.csr is the certificate request
rootCA.crt is the public key

Everything uses x.509 certificate format.

A Root Certificate Authority (Root CA) is a trusted entity that issues digital certificates used in the public key infrastructure.
The Root CA issues certificates to intermediate CAs, which, in turn, may issue certificates to end entities (such as servers or clients).

The rootCA public key is used to validate the issued certificates.

Second, we generate server and client certificates

# Generate client mTLS certificate
openssl genpkey -algorithm RSA -out client-private-key.pem
openssl req -new -key client-private-key.pem -out client.csr -subj "/CN=client"
openssl x509 -req -days 365 -in client.csr -CA rootCA.crt -CAkey rootCA-private-key.pem -CAcreateserial -out client.crt

# Generate server certificate
openssl genpkey -algorithm RSA -out server-private-key.pem
openssl req -new -key server-private-key.pem -out server.csr -subj "/CN=localhost"
openssl x509 -req -days 365 -in server.csr -CA rootCA.crt -CAkey rootCA-private-key.pem -CAcreateserial -out server.crt
Enter fullscreen mode Exit fullscreen mode

They are signed by the rootCA private key.
The only different is the CN (Common Name), which needs to be the domain name when used on the server.

Using Certificates on Server and Client

Here is a basic server implementation using koa

const options = {
  key: fs.readFileSync(config.SERVER_PRIVATE_KEY),
  cert: fs.readFileSync(config.SERVER_CERT),
  ca: fs.readFileSync(config.ROOT_CA_CERT),
    requestCert: true,
    rejectUnauthorized: true,
  };

const server = https.createServer(options, app.callback());

app.use(async (ctx) => {
  console.log('hello');
  ctx.body = 'Hello, secure world!';
  ctx.status = 200;
});

server.listen(port, () => {
  console.log(`Server running at https://localhost:${port}`);
});
Enter fullscreen mode Exit fullscreen mode

We pass the server's private and public key and also the rootCA public key to create the HTTPS server.

Here is an HTTP client using fetch

const apiUrl = 'https://localhost:3000'; // Replace with your server URL

const clientCert = fs.readFileSync(config.CLIENT_CERT);
const clientKey = fs.readFileSync(config.CLIENT_PRIVATE_KEY);
const rootCA = fs.readFileSync(config.ROOT_CA_CERT);

const agent = new https.Agent({
  cert: clientCert,
  key: clientKey,
  ca: rootCA,
  rejectUnauthorized: false
});

const run = async () => {
  const response = await fetch(apiUrl, { agent });
  const data = await response.text();
  console.log({
    response,
    data,
  });
}
Enter fullscreen mode Exit fullscreen mode

We pass the client's private and public key and also the rootCA public key to the client.

If everything goes right, you can spin up your server and make an HTTP request using fetch to your server.
If you remove the client certificates the request will fail:

FetchError: request to https://localhost:3000/ failed, reason: socket hang up
Enter fullscreen mode Exit fullscreen mode

In Summary

Certificates can be scary content. But they are just math.
They are the basis for providing security to our services and integrations.
If you want to work at Fintechs, you need to know at least the basics of Cryptography.


Woovi
Woovi is a Startup that enables shoppers to pay as they like. To make this possible, Woovi provides instant payment solutions for merchants to accept orders.

If you want to work with us, we are hiring!


Image By vecstock

Top comments (0)