{"id":18709186,"url":"https://github.com/jojoee/ssl-mutual-authentication","last_synced_at":"2025-11-09T11:30:25.384Z","repository":{"id":237413028,"uuid":"644668127","full_name":"jojoee/ssl-mutual-authentication","owner":"jojoee","description":null,"archived":false,"fork":false,"pushed_at":"2023-05-26T03:36:20.000Z","size":79,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-12-28T07:42:20.246Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jojoee.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-24T02:23:21.000Z","updated_at":"2023-08-03T06:37:30.000Z","dependencies_parsed_at":"2024-05-01T14:24:38.877Z","dependency_job_id":"cf94fa1a-2077-474f-b139-3cc879ed44fd","html_url":"https://github.com/jojoee/ssl-mutual-authentication","commit_stats":null,"previous_names":["jojoee/ssl-mutual-authentication"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jojoee%2Fssl-mutual-authentication","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jojoee%2Fssl-mutual-authentication/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jojoee%2Fssl-mutual-authentication/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jojoee%2Fssl-mutual-authentication/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jojoee","download_url":"https://codeload.github.com/jojoee/ssl-mutual-authentication/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239571578,"owners_count":19661165,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-07T12:26:39.408Z","updated_at":"2025-11-09T11:30:25.344Z","avatar_url":"https://github.com/jojoee.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SSL mutual authentication\n\nSSL Mutual authentication where server and client verify each other, instead of verifying server as usual.\n\nIn practical daily SSL, browser and website for example. Browser only need to verify server. But in some cases server want to verify client as well. Here is an tutorial on how to setup mutual authentication on Node.js with CA (Certificate Authority) and without CA (self-signed). In this tutorial\n- `KEY` = private key\n- `CRT` = certificate\n- `CSR` = certificate signing request\n\n## Note\n\n- Clients use the server's public key in this certificate to encrypt data that only the server can decrypt with its private key, this is why we have to sent `CRT` to client.\n- We generate `KEY`\n- We generate `CSR` from `KEY`\n- We generate `CRT` from `CSR` and sign with `KEY`\n- We generate `PKC12` from `CRT` and `KEY`\n- We convert `PKC12` to `JKS`\n- Privacy-Enhanced Mail (PEM) is a de facto file format for storing and sending cryptographic keys, certificates, and other data, based on a set of 1993 IETF standards defining \"privacy-enhanced mail.\"\n- Transport Layer Security (TLS) is an encryption protocol in wide use on the Internet. TLS, which was formerly called SSL\n- self-signed TLS/SSL certificate is not signed by a publicly trusted certificate authority (CA) but instead by the developer or company that is responsible for the website\n- TrustManager is Java specific interface to verify the ceritificate which require Truststore file\n- Truststore file in Java = CA-Certificate file\n- Keystore = `JKS` (Java KeyStore)\n- `.pem` = file that contain different types of cryptographic objects, including the same X.509 certificate in a base64-encoded format\n- `CRT` = file that contain an `X.509 certificate` for SSL/TLS\n- `CRT` = certificate issued to a specific domain contains public key, subject details (such as the server's domain name), the issuing authority, the validity period, etc.\n- CA certificate = signed and issued the server certificate, contains CA's public key, subject details (such as the CA's name), the issuer (usually itself for Root CAs), validity period, etc.\n- The CA certificate is used for verifying the authenticity of the server certificate. Browsers and operating systems maintain a list of trusted CAs\n\n## Prerequisite\n\n1. Install [Node.js](https://nodejs.org/en), and `npm install` to install packages needed\n2. Install uility tool: `openssl`, `keytool`, `md5`\n\n## Getting Started\n\n### Step 1) Add test domain into `hosts` file\n\n```bash\n# as root\necho '127.0.0.1 server.aaa.com' \u003e\u003e /etc/hosts\necho '127.0.0.1 client.bbb.com' \u003e\u003e /etc/hosts\ncat /etc/hosts # to verify\n```\n\n### Step 2) Generate server-CA-KEY and server-CA-CRT\n\n```bash\nopenssl req -new -x509 -days 365 -keyout server-ca-key.pem -out server-ca-crt.pem # 123456\n# Generating a RSA private key\n# .........................................................+++++\n# ......................+++++\n# writing new private key to 'server-ca-key.pem'\n# Enter PEM pass phrase:\n# Verifying - Enter PEM pass phrase:\n# -----\n# You are about to be asked to enter information that will be incorporated\n# into your certificate request.\n# What you are about to enter is what is called a Distinguished Name or a DN.\n# There are quite a few fields but you can leave some blank\n# For some fields there will be a default value,\n# If you enter '.', the field will be left blank.\n# -----\n# Country Name (2 letter code) [AU]:IT\n# State or Province Name (full name) [Some-State]:Florence\n# Locality Name (eg, city) []:Campi Bisenzio\n# Organization Name (eg, company) [Internet Widgits Pty Ltd]:AAA Ltd\n# Organizational Unit Name (eg, section) []:DevOps\n# Common Name (e.g. server FQDN or YOUR name) []:aaa.com\n# Email Address []:info@aaa.com\n```\n\n### Step 3) Generate server-KEY an server-CSR\n\n```bash\nopenssl genrsa -out server-key.pem 4096\nopenssl req -new -sha256 -key server-key.pem -out server-csr.pem\n# You are about to be asked to enter information that will be incorporated\n# into your certificate request.\n# What you are about to enter is what is called a Distinguished Name or a DN.\n# There are quite a few fields but you can leave some blank\n# For some fields there will be a default value,\n# If you enter '.', the field will be left blank.\n# -----\n# Country Name (2 letter code) [AU]:IT\n# State or Province Name (full name) [Some-State]:Florence\n# Locality Name (eg, city) []:Campi Bisenzio\n# Organization Name (eg, company) [Internet Widgits Pty Ltd]:AAA Ltd\n# Organizational Unit Name (eg, section) []:DevOps\n# Common Name (e.g. server FQDN or YOUR name) []:server.aaa.com\n# Email Address []:info@aaa.com\n\n# Please enter the following 'extra' attributes\n# to be sent with your certificate request\n# A challenge password []:\n# An optional company name []:\n```\n\n### Step 4) Generate server-CRT with CA and without CA (self-signed)\n\nServer using `server-ca-key.pem` or `server-key.pem` (self-signed) to sign certificate.\n\n```bash\n# CA\nopenssl x509 -req -days 365 -in server-csr.pem -CA server-ca-crt.pem -CAkey server-ca-key.pem -CAcreateserial -out server-ca-signed-crt.pem\nopenssl verify -CAfile server-ca-crt.pem server-ca-signed-crt.pem\n\n# self-signed\nopenssl x509 -req -days 365 -in server-csr.pem -signkey server-key.pem -out server-self-signed-crt.pem\nopenssl verify -CAfile server-self-signed-crt.pem server-self-signed-crt.pem\n```\n\n### Step 5) Generate client-CA-KEY and server-CA-CRT\n\n```bash\nopenssl req -new -x509 -days 365 -keyout client-ca-key.pem -out client-ca-crt.pem\n# Generating a RSA private key\n# ............+++++\n# ............................................+++++\n# writing new private key to 'client-ca-key.pem'\n# Enter PEM pass phrase:\n# Verifying - Enter PEM pass phrase:\n# -----\n# You are about to be asked to enter information that will be incorporated\n# into your certificate request.\n# What you are about to enter is what is called a Distinguished Name or a DN.\n# There are quite a few fields but you can leave some blank\n# For some fields there will be a default value,\n# If you enter '.', the field will be left blank.\n# -----\n# Country Name (2 letter code) [AU]:IT\n# State or Province Name (full name) [Some-State]:Rome\n# Locality Name (eg, city) []:Rome\n# Organization Name (eg, company) [Internet Widgits Pty Ltd]:BBB Ltd\n# Organizational Unit Name (eg, section) []:\n# Common Name (e.g. server FQDN or YOUR name) []:bbb.com\n# Email Address []:info@bbb.com\n```\n\n### Step 6) Genearate server-KEY an server-CSR\n\n```bash\nopenssl genrsa -out client-key.pem 4096\nopenssl req -new -sha256 -key client-key.pem -out client-csr.pem\n# You are about to be asked to enter information that will be incorporated\n# into your certificate request.\n# What you are about to enter is what is called a Distinguished Name or a DN.\n# There are quite a few fields but you can leave some blank\n# For some fields there will be a default value,\n# If you enter '.', the field will be left blank.\n# -----\n# Country Name (2 letter code) [AU]:IT\n# State or Province Name (full name) [Some-State]:Rome\n# Locality Name (eg, city) []:Rome\n# Organization Name (eg, company) [Internet Widgits Pty Ltd]:BBB Ltd\n# Organizational Unit Name (eg, section) []:\n# Common Name (e.g. server FQDN or YOUR name) []:client.bbb.com\n# Email Address []:info@bbb.com\n\n# Please enter the following 'extra' attributes\n# to be sent with your certificate request\n# A challenge password []:\n# An optional company name []:\n```\n\n### Step 7) Generate client-CRT (certificate) with CA and without CA (self-signed)\n\nClient using `client-ca-key.pem` or `client-key.pem` (self-signed) to sign certificate.\n\n```bash\n# CA\nopenssl x509 -req -days 365 -in client-csr.pem -CA client-ca-crt.pem -CAkey client-ca-key.pem -CAcreateserial -out client-ca-signed-crt.pem\nopenssl verify -CAfile client-ca-crt.pem client-ca-signed-crt.pem\n\n# self-signed\nopenssl x509 -req -days 365 -in client-csr.pem -signkey client-key.pem -out client-self-signed-crt.pem\nopenssl verify -CAfile client-self-signed-crt.pem client-self-signed-crt.pem\n```\n\n## How to run and test\n\n```bash\n# Server CA, Client CA\nnode server.js --key=server-key.pem --cert=server-ca-signed-crt.pem --ca=client-ca-crt.pem --requestCert --rejectUnauthorized\nnode client.js --key=client-key.pem --cert=client-ca-signed-crt.pem --ca=server-ca-crt.pem --host=server.aaa.com:3443 # ok-authorized-client\n\n# Server self-signed, Client self-signed\nnode server.js --key=server-key.pem --cert=server-self-signed-crt.pem --ca=client-self-signed-crt.pem --requestCert --rejectUnauthorized\nnode client.js --key=client-key.pem --cert=client-self-signed-crt.pem --ca=server-self-signed-crt.pem --host=server.aaa.com:3443 # ok-authorized-client\n\n# Server CA, Client self-signed\nnode server.js --key=server-key.pem --cert=server-ca-signed-crt.pem --ca=client-self-signed-crt.pem --requestCert --rejectUnauthorized\nnode client.js --key=client-key.pem --cert=client-self-signed-crt.pem --ca=server-ca-crt.pem --host=server.aaa.com:3443 # ok-authorized-client\n\n# Server self-signed, Client CA\nnode server.js --key=server-key.pem --cert=server-self-signed-crt.pem --ca=client-ca-crt.pem --requestCert --rejectUnauthorized\nnode client.js --key=client-key.pem --cert=client-ca-signed-crt.pem --ca=server-self-signed-crt.pem --host=server.aaa.com:3443 # ok-authorized-client\n```\n\n## JKS (Java KeyStore)\n\n`JKS` is a Java specific format. `KEY` + `CRT` =\u003e `PKC12` =\u003e `JKS`, change `yourkeystorepassword` password to your choice.\n\n```bash\n# Server\nrm ./server-keystore.p12 ./server-keystore.jks\nopenssl pkcs12 -export -in server-ca-signed-crt.pem -inkey server-key.pem -out server-keystore.p12 -name aaa-alt -password pass:serverkeystorepassword\nkeytool -importkeystore -srckeystore server-keystore.p12 -srcstoretype PKCS12 -destkeystore server-keystore.jks -deststoretype JKS -srcstorepass serverkeystorepassword -deststorepass serverkeystorepassword\n# Importing keystore server-keystore.p12 to server-keystore.jks...\n# Entry for alias aaa-alt successfully imported.\n# Import command completed:  1 entries successfully imported, 0 entries failed or cancelled\n\n# Client\nrm ./client-keystore.p12 ./client-keystore.jks\nopenssl pkcs12 -export -in client-ca-signed-crt.pem -inkey client-key.pem -out client-keystore.p12 -name bbb-alt -password pass:clientkeystorepassword\nkeytool -importkeystore -srckeystore client-keystore.p12 -srcstoretype PKCS12 -destkeystore client-keystore.jks -deststoretype JKS -srcstorepass clientkeystorepassword -deststorepass clientkeystorepassword\n# Importing keystore client-keystore.p12 to client-keystore.jks...\n# Entry for alias bbb-alt successfully imported.\n# Import command completed:  1 entries successfully imported, 0 entries failed or cancelled\n```\n\n## Validate if `KEY` and `CSR` and `CRT` file are comptible to each others\n\n\"modulus\" should be the same to all. If the module match, it means they are meant to be used together.\n\n```bash\n# Server\nopenssl rsa -noout -modulus -in server-key.pem | openssl md5\n# (stdin)= b8383414c800f6b8bbe6c8058b7d4f10\nopenssl req -noout -modulus -in server-csr.pem | openssl md5\n# (stdin)= b8383414c800f6b8bbe6c8058b7d4f10\nopenssl x509 -noout -modulus -in server-ca-signed-crt.pem | openssl md5\n# (stdin)= b8383414c800f6b8bbe6c8058b7d4f10\nopenssl x509 -noout -modulus -in server-self-signed-crt.pem | openssl md5\n# (stdin)= b8383414c800f6b8bbe6c8058b7d4f10\n\n# Client\nopenssl rsa -noout -modulus -in client-key.pem | openssl md5\n# (stdin)= 5b87a3317a392f1350d54f8f99837d8b\nopenssl req -noout -modulus -in client-csr.pem | openssl md5\n# (stdin)= 5b87a3317a392f1350d54f8f99837d8b\nopenssl x509 -noout -modulus -in client-ca-signed-crt.pem | openssl md5\n# (stdin)= 5b87a3317a392f1350d54f8f99837d8b\nopenssl x509 -noout -modulus -in client-self-signed-crt.pem | openssl md5\n# (stdin)= 5b87a3317a392f1350d54f8f99837d8b\n```\n\n## Validate if `JSK` with others\n\nNeed to convert back `JKS` =\u003e `PCK12` =\u003e `CRT` + `KEY`\n\n```bash\nrm ./degenerate-server-keystore.p12\nkeytool -importkeystore -srckeystore server-keystore.jks -destkeystore degenerate-server-keystore.p12 -srcstoretype jks -srcstorepass serverkeystorepassword -deststoretype pkcs12 -deststorepass serverkeystorepassword -alias aaa-alt\n# Importing keystore server-keystore.jks to degenerate-server-keystore.p12...\nopenssl pkcs12 -in degenerate-server-keystore.p12 -nocerts -nodes -passin pass:serverkeystorepassword | openssl rsa \u003e degenerate-server-key.pem\nopenssl pkcs12 -in degenerate-server-keystore.p12 -nokeys -passin pass:serverkeystorepassword | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' \u003e degenerate-server-ca-signed-crt.pem\n\n# It should be the same\nmd5 server-key.pem\n# MD5 (server-key.pem) = 99bd7b72b95e9f739694d2b0bc131a8e\nmd5 degenerate-server-key.pem\n# MD5 (degenerate-server-key.pem) = 99bd7b72b95e9f739694d2b0bc131a8e\n\n# It should be the same\nmd5 server-ca-signed-crt.pem\n# MD5 (server-ca-signed-crt.pem) = a56809780290fda9736c08841a8b1330\nmd5 degenerate-server-ca-signed-crt.pem\n# MD5 (degenerate-server-ca-signed-crt.pem) = a56809780290fda9736c08841a8b1330\n```\n\n## Read `JKS` file\n\n```bash\nkeytool -v -list -keystore server-keystore.jks -storepass \"serverkeystorepassword\" \u003e server-keystore.jks.txt\nkeytool -v -list -keystore client-keystore.jks -storepass \"clientkeystorepassword\" \u003e client-keystore.jks.txt\n```\n\n## Read `CRT` file\n\n```bash\n# Server\nopenssl x509 -in server-ca-signed-crt.pem -text -noout \u003e server-ca-signed-crt.pem.txt\nopenssl x509 -in server-self-signed-crt.pem -text -noout \u003e server-self-signed-crt.pem.txt\n\n# Client\nopenssl x509 -in client-ca-signed-crt.pem -text -noout \u003e client-ca-signed-crt.pem.txt\nopenssl x509 -in client-self-signed-crt.pem -text -noout \u003e client-self-signed-crt.pem.txt\n```\n\n## Check if server have SSL setup\n\n```bash\nopenssl s_client -connect server.aaa.com:3443\nopenssl s_client -connect server.aaa.com:3443 | openssl x509 -noout -dates\n# depth=0 C = IT, ST = Florence, L = Campi Bisenzio, O = AAA Ltd, OU = DevOps, CN = server.aaa.com, emailAddress = info@aaa.com\n# verify error:num=20:unable to get local issuer certificate\n# verify return:1\n# depth=0 C = IT, ST = Florence, L = Campi Bisenzio, O = AAA Ltd, OU = DevOps, CN = server.aaa.com, emailAddress = info@aaa.com\n# verify error:num=21:unable to verify the first certificate\n# verify return:1\n# depth=0 C = IT, ST = Florence, L = Campi Bisenzio, O = AAA Ltd, OU = DevOps, CN = server.aaa.com, emailAddress = info@aaa.com\n# verify return:1\n# 140704365749824:error:1409445C:SSL routines:ssl3_read_bytes:tlsv13 alert certificate required:ssl/record/rec_layer_s3.c:1544:SSL alert number 116\n# notBefore=May 25 11:23:02 2023 GMT\n# notAfter=May 24 11:23:02 2024 GMT\n```\n\n## Check if server require client-CRT\n\n```bash\ncurl -k https://server.aaa.com:3443\n# soft reject if \"--requestCert\", client able to connect, server knows the client is unauthorized and decide to how to with it\n# hard reject if \"--requestCert --rejectUnauthorized\", client not able to connect\n```\n\n## Reference\n\n- https://www.cloudflare.com/learning/access-management/what-is-mutual-tls/\n- https://www.f5.com/labs/learning-center/what-is-mtls\n- https://www.matteomattei.com/client-and-server-ssl-mutual-authentication-with-nodejs/\n- https://gist.github.com/pcan/e384fcad2a83e3ce20f9a4c33f4a13ae\n- https://engineering.circle.com/https-authorized-certs-with-node-js-315e548354a2\n- https://medium.com/swlh/learning-configuring-https-for-node-js-5097e44320e3\n- https://levelup.gitconnected.com/how-to-resolve-certificate-errors-in-nodejs-app-involving-ssl-calls-781ce48daded\n- https://stackoverflow.com/questions/21397809/create-a-trusted-self-signed-ssl-cert-for-localhost-for-use-with-express-node\n- https://nodejs.org/api/tls.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjojoee%2Fssl-mutual-authentication","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjojoee%2Fssl-mutual-authentication","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjojoee%2Fssl-mutual-authentication/lists"}