Skip to content

SakerGT/mtls-node-example

Repository files navigation

Mutual TLS and certificate validation checks

This project uses Node JS server and client to demonstrate mTLS and validation of additional certificate properties. It is intended to be used as examples / training material only.

There are 5 exercises to demonstrate mutual authentication certificate checks that can be done:

  1. Trusted issuer
  2. Common Name check
  3. Subject Alternative Name (SAN) check
  4. Public key fingerprint
  5. Certificate fingerprint

The below gives some context and motivation for use of mTLS and these examples. Also included is instructions for generating your own dummy CA and certificates should you wish to.

Contributions are encouraged, please see the contribution guidelines.

Overview

This project demonstrates the creation of mutual trust between client and server, using a shared certificate authority (CA). The CA is trusted by both client and server, and both use certificates that are signed by the CA. The certificates are used to establish identity between client and server (authenticate their respective identities).

The following shows this in an abstract way:

CA-overview

Motivation

You may be wondering what the point of this is? Well, mTLS is really the only way to be certain (well, relatively certain) that the network path is as intended - that the communicating partners are in fact, who they claim to be.

The following shows the relative strengths of checks that can be performed using mTLS:

X.509 Certificate Verification Strength

Dependencies

Ensure you have an install of:

  • NodeJS 19 or later
  • OpenSSL command line tools

Setup

Create a CA

openssl req -new -x509 -days 3650 -keyout ca-key.pem -out ca-crt.pem

Password defaults to cap@ssword*&. Uses democa.tlsdemo.co.nz for FQDN.

Prepare certificates

Server certificate

Generate Server Key:

openssl genrsa -out server-key.pem 4096

Generate Server certificate signing request:

openssl req -new -key server-key.pem -out server-csr.pem

Specify server Common Name, like 'localhost' or 'server.localhost'. The client will verify this, so make sure you have a valid DNS name for this. For this example, do not set the challenge password.

Alternatively, specify required information in single command, including subject alternative names:

openssl req -new -subj "/C=NZ/ST=Wellington/L=Wellington/O=Demo Servers Ltd/OU=Web Servers/CN=www.demoservers.co.nz/emailAddress=webmaster@demoservers.co.nz" -addext "subjectAltName=DNS:test.demoservers.co.nz,DNS:www.demoservers.co.nz" -key server-key.pem -out server-csr.pem

Note: OpenSSL does not copy subjectAltName from CSR to certificate. See the extensions.cnf file for required configuration.

Optional: Print the CSR as text using:

openssl req -text -noout -verify -in server-csr.pem

Sign certificate using the CA:

openssl x509 -req -days 90 -in server-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out server-crt.pem

Optionally, sign including extensions:

openssl x509 -req -days 90 -in server-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out server-crt.pem -extfile .\extensions.cnf

  • insert CA Password

Verify server certificate:

openssl verify -CAfile ca-crt.pem server-crt.pem

Generate server certificate public key ping-sha256 (works in Linux / Mac only):

openssl x509 -in server\-crt.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

Get server certificate fingerprint:

openssl x509 -noout -fingerprint -sha256 -inform pem -in server-crt.pem

Client certificate

Generate Client Key:

openssl genrsa -out client1-key.pem 4096

Generate Client certificate signing request:

openssl req -new -key client1-key.pem -out client1-csr.pem

Specify client Common Name, like 'client.localhost'. Must be a valid DNS name for this example.

Sign certificate using the CA:

openssl x509 -req -days 90 -in client1-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out client1-crt.pem

  • insert CA Password

Verify client certificate:

openssl verify -CAfile ca-crt.pem client1-crt.pem

Generate client certificate public key pin-sha256 (works in Linux / Mac only):

openssl x509 -in client1-crt.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

Get client certificate fingerprint:

openssl x509 -noout -fingerprint -sha256 -inform pem -in client1-crt.pem

Testing with node

First run the server:

node server.js

Then run the client

node client.js

Adjust any of the tests in the body of the code to confirm behaviour

Testing server with curl

From the CA directory:

curl -v --cacert ca-crt.pem --cert client1-crt.pem --key client1-key.pem -k https://localhost:8000

To include OCSP check:

curl -v --cacert ca-crt.pem --cert client1-crt.pem --key client1-key.pem -k --cert-status https://localhost:8000

HTTP response codes

This example returns a 200 OK HTTP response for success, and 401 Not Authorised on failure, and as required by RFC7230 a WWW-Authenticate response header is included. However, no IANA registered authentication scheme is available, so a custom scheme TLS with realm=tls=demo is returned. This should not be interpreted as requiring the client certificate in an Authorization header (which is the normal interpretation WWW-Authenticate challenge).

It is also possible to use 403 Forbidden to indicate a failure, however this is considered to be less friendly to clients, and less informative that authentication can be retried with different TLS credentials (a different certificate/key pair). Nevertheless, 403 is completely valid and both 401 and 403 are seen in practise.

About

mTLS demo with NodeJS

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published