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.