In Mutual TLS both Client and Server have a certificate and both sides authenticate using their public/private key pair. Below are the handshake steps in simplistic way.
- Client connects to server
- Server presents its TLS certificate
- Client verifies the server’s certificate
- Client presents its TLS certificate
- Server verifies the client’s certificate
- Server grants access
- Client and server exchange information over encrypted TLS connection
Below are the steps and commands
Generate Server Certificate
Server Details
- Organization Name: Bobby Dreamer Inc.,
- Organizational Unit Name: Crypto
Generate Certification Authority (CA) certificate for server
- Password: super
- Common Name(CN): localhost
openssl req -new -x509 -days 365 -keyout server-ca-key.pem -out server-ca-crt.pem
# Output
Generating a RSA private key
....+++++
.............................................+++++
writing new private key to 'server-ca-key.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:IN
State or Province Name (full name) [Some-State]:TN
Locality Name (eg, city) []:Chennai
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Bobby Dreamer Inc.,
Organizational Unit Name (eg, section) []:Crypto
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:
Generating a private key
openssl genrsa -out server-key.pem 4096
# Output
Generating RSA private key, 4096 bit long modulus (2 primes)
.............................++++
..............................++++
e is 65537 (0x010001)
Generate Certificate Signing Request (CSR)
- Common Name(CN): server.localhost
- do not insert the challenge password
openssl req -new -sha256 -key server-key.pem -out server-csr.pem
# Output
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:IN
State or Province Name (full name) [Some-State]:TN
Locality Name (eg, city) []:Chennai
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Bobby Dreamer Inc.,
Organizational Unit Name (eg, section) []:Crypto
Common Name (e.g. server FQDN or YOUR name) []:server.localhost
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Generate the server certificate from CSR
- Type in the CA Password:super
openssl x509 -req -days 365 -in server-csr.pem -CA server-ca-crt.pem -CAkey server-ca-key.pem -CAcreateserial -out server-crt.pem
# Output
Signature ok
subject=C = IN, ST = TN, L = Chennai, O = "Bobby Dreamer Inc., ", OU = Crypto, CN = server.localhost
Getting CA Private Key
Enter pass phrase for server-ca-key.pem:
Verify certificate signature
Using the below command you can verify the certificate signature against the CA
openssl verify -CAfile server-ca-crt.pem server-crt.pem
# Output
server-crt.pem: OK
Generate Client Certificate
Client details
- Organization Name: Browsers Inc.,
- Organizational Unit Name: Security
Generate Certification Authority (CA) certificate for client
- Password: client
- Common Name(CN): browser.com
openssl req -new -x509 -days 365 -keyout client-ca-key.pem -out client-ca-crt.pem
# Output
Generating a RSA private key
......+++++
............+++++
writing new private key to 'client-ca-key.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:IN
State or Province Name (full name) [Some-State]:TN
Locality Name (eg, city) []:Chennai
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Browsers Inc.,
Organizational Unit Name (eg, section) []:Security
Common Name (e.g. server FQDN or YOUR name) []:browser.com
Email Address []:
Generate a private key
openssl genrsa -out client-key.pem 4096
# Output
Generating RSA private key, 4096 bit long modulus (2 primes)
...............................................................................................................................++++
..............................++++
e is 65537 (0x010001)
Generate Certificate Signing Request (CSR)
- Common Name(CN): client.browser.com
- do not insert the challenge password
openssl req -new -sha256 -key client-key.pem -out client-csr.pem
# Output
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:IN
State or Province Name (full name) [Some-State]:TN
Locality Name (eg, city) []:Chennai
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Browsers Inc.,
Organizational Unit Name (eg, section) []:Security
Common Name (e.g. server FQDN or YOUR name) []:client.browser.com
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Generate client certificate from CSR
- Type in the CA Password:client
openssl x509 -req -days 365 -in client-csr.pem -CA client-ca-crt.pem -CAkey client-ca-key.pem -CAcreateserial -out client-crt.pem
# Output
Signature ok
subject=C = IN, ST = TN, L = Chennai, O = "Browsers Inc.,", OU = Security, CN = client.browser.com
Getting CA Private Key
Enter pass phrase for client-ca-key.pem:
Verify certificate signature
openssl verify -CAfile client-ca-crt.pem client-crt.pem
# Output
client-crt.pem: OK
Test
Code is available here in Github
- Start the server.js script
node server.js
- In another terminal window, start the
node client.js
Errors you might get
D:\BigData\14.Nodejs\17.Certificates\b.client-server_certificate2>node client.js
TLS Socket ERROR (Error: getaddrinfo ENOTFOUND server.localhost)
Solution
In Windows, go to C:\Windows\System32\drivers\etc
- Backup the existing
hosts
file - Edit the file and add the below two lines
127.0.0.1 server.localhost
127.0.0.1 client.browser.com
It should look like below
Rerun the client script node client.js
D:\BigData\14.Nodejs\17.Certificates\b.client-server_certificate2>node client.js
TLS Connection established successfully!
Response statusCode: 200
Response headers: {
date: 'Mon, 30 Aug 2021 15:25:24 GMT',
connection: 'close',
'transfer-encoding': 'chunked'
}
Server Host Name: server.localhost
Received message: OK!
TLS Connection closed!
In the server side, you will get response like below,
D:\BigData\14.Nodejs\17.Certificates\b.client-server_certificate2>node server.js
Mon Aug 30 2021 20:55:23 GMT+0530 (India Standard Time) ::ffff:127.0.0.1 GET /
CURL
Tried to connect to server using CURL it did not work. Did not attempt to do any further investigating as it looks like another whole subject to learn.
curl -k https://127.0.0.1:8888 -v --cacert ./certs/server-ca-crt.pem --key ./certs/client-key.pem --cert ./certs/client-crt.pem
# Output
* Rebuilt URL to: https://127.0.0.1:8888/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8888 (#0)
* schannel: SSL/TLS connection with 127.0.0.1 port 8888 (step 1/3)
* schannel: disabled server certificate revocation checks
* schannel: verifyhost setting prevents Schannel from comparing the supplied target name with the subject names in server certificates.
* schannel: using IP address, SNI is not supported by OS.
* schannel: sending initial handshake data: sending 153 bytes...
* schannel: sent initial handshake data: sent 153 bytes
* schannel: SSL/TLS connection with 127.0.0.1 port 8888 (step 2/3)
* schannel: failed to receive handshake, need more data
* schannel: SSL/TLS connection with 127.0.0.1 port 8888 (step 2/3)
* schannel: encrypted data got 1970
* schannel: encrypted data buffer: offset 1970 length 4096
* schannel: a client certificate has been requested
* schannel: SSL/TLS connection with 127.0.0.1 port 8888 (step 2/3)
* schannel: encrypted data buffer: offset 1970 length 4096
* schannel: sending next handshake data: sending 100 bytes...
* schannel: SSL/TLS connection with 127.0.0.1 port 8888 (step 2/3)
* schannel: encrypted data got 7
* schannel: encrypted data buffer: offset 7 length 4096
* schannel: next InitializeSecurityContext failed: SEC_E_ILLEGAL_MESSAGE (0x80090326) - This error usually occurs when a fatal SSL/TLS alert is received (e.g. handshake failed). More detail may be available in the Windows System event log.
* Closing connection 0
* schannel: shutting down SSL/TLS connection with 127.0.0.1 port 8888
* schannel: clear security context handle
curl: (35) schannel: next InitializeSecurityContext failed: SEC_E_ILLEGAL_MESSAGE (0x80090326) - This error usually occurs when a fatal SSL/TLS alert is received (e.g. handshake failed). More detail may be available in the Windows System event log.