Summary
This post will cover the mutual TLS/client-side certificate approach to API authentication. This is an authentication scheme that's suitable for machine to machine authentication of a limited number of clients. I'll demonstrate the approach with Node.js implementations of the server and client using self-signed certificates.Message Flow
Diagram below depicting the message exchanges (from a CURL session) for a mutual TLS authentication.Generating Self-Signed Certs
Below is a sequence of OpenSSL commands to generate the server-side private key and self-signed public certificate, the client-side private key and certificate signing request, and finally client-side certificate signing by the server-side cert.#generate server private key and cert openssl req -x509 -newkey rsa:4096 -keyout serverKey.pem -out serverCert.pem -nodes -days 365 -subj "/CN=localhost" #generate client private key and cert signing request openssl req -newkey rsa:4096 -keyout clientKey.pem -out clientCsr.pem -nodes -subj "/CN=Client" #sign client cert openssl x509 -req -in clientCsr.pem -CA serverCert.pem -CAkey serverKey.pem -out clientCert.pem -set_serial 01 -days 365
Server Snippet
Node.js implementation of a REST server that will request mutual TLS/client-side cert. Highlighted areas are of note for mutual TLS.const https = require('https');
const express = require('express');
const fs = require('fs');
const port = 8443;
const key = fs.readFileSync('serverKey.pem');
const cert = fs.readFileSync('serverCert.pem');
const options = {
key: key,
cert: cert,
requestCert: true,
ca: [cert]
};
let kvpStore = {};
const app = express();
app.use(express.json());
//create
app.post('/kvp', (req, res) => {
const key = req.body.key;
const value = req.body.value;
if (key && value) {
if (key in kvpStore) {
res.status(400).json({error: 'kvp already exists'});
}
else {
kvpStore[key] = value;
res.status(201).json({key: key, value: kvpStore[key]});
}
}
else {
res.status(400).json({error: 'missing key value pair'});
}
});
CURL Client Commands
#CREATE curl -v -k --key clientKey.pem --cert clientCert.pem -H "Content-Type: application/json" -d '{"key":"1", "value":"abc"}' https://localhost:8443/kvp #RETRIEVE curl -v -k --key clientKey.pem --cert clientCert.pem https://localhost:8443/kvp/1 #UPDATE curl -v -k --key clientKey.pem --cert clientCert.pem -X PUT -H "Content-Type: application/json" -d '{"key":"1", "value":"def"}' https://localhost:8443/kvp #DELETE curl -v -k --key clientKey.pem --cert clientCert.pem -X DELETE https://localhost:8443/kvp/1
Client Snippet
Node.js implementation of REST client supporting mutual TLS. Again, highlighted areas depicting where mutual TLS-specific configuration is necessary.const https = require('https'); const fs = require('fs'); const fetch = require('node-fetch'); const key = fs.readFileSync('clientKey.pem'); const cert = fs.readFileSync('clientCert.pem'); const options = { key: key, cert: cert, rejectUnauthorized: false }; const url = 'https://localhost:8443/kvp/'; const tlsAgent = new https.Agent(options) async function create(kvp) { const response = await fetch(url, { method: 'POST', body: JSON.stringify(kvp), headers: {'Content-Type': 'application/json'}, agent: tlsAgent }); const json = await response.json(); console.log(`CREATE - ${response.status} ${JSON.stringify(json)}`); }
Source
https://github.com/joeywhelan/mutualtlsCopyright ©1993-2024 Joey E Whelan, All rights reserved.