2-way SSL Authentication

Hello,

I'm trying to add 2-way SSL authentication on my Express REST API and fixed client. I followed this in order to generate the appropriate certificates and keys for server-side and client-side and this is my code on the server-side:

const https = require('https')
const express = require('express');
const app = express();
const port = 3000;
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(morgan('combined'));
var helmet = require('helmet')
app.use(helmet())

app.get('/endpoint', async function(req, res, next) {
       res.json({ message: 'Connection has been established successfully.' })
});

const sslServer = https.createServer({
    key: fs.readFileSync(path.join(__dirname, 'cert', 'server-key.pem')),
    cert: fs.readFileSync(path.join(__dirname, 'cert', 'server-crt.pem')),
    ca: fs.readFileSync(path.join(__dirname,'cert', 'ca-crt.pem')),
    requestCert: true, 
    rejectUnauthorized: true
}, app);

sslServer.listen(port, () => console.log(`Example app listening on port ${port}!`));

And on the client side:

const httpsAgent = new https.Agent({
            key: fs.readFileSync(path.join(__dirname,'..','..','..','client1-key.pem')), 
            cert: fs.readFileSync(path.join(__dirname,'..','..','..','client1-crt.pem')),
            ca: fs.readFileSync(path.join(__dirname,'..','..','..','ca-crt.pem')),
            passphrase: "password",
            rejectUnauthorized:true,
            requestCert:true
          });

axios.post('https://IP:PORT/endpoint',{
                data: {
                    data1: data1,
                    data2: data2,
                },
                httpsAgent: httpsAgent
              })

When I do this, I get the following:

Error: write EPROTO 140333960492168:error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE:../../third_party/boringssl/src/ssl/tls_record.cc:592:SSL alert number 40

    at WriteWrap.onWriteComplete [as oncomplete] (internal/stream_base_commons.js:87:16)

I verified all my certificates and keys using (both the following send an 'okay' response):

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

My question is, what is the significance and specifications of the CA Common Names for the certificate authority (ca-crt.pem), server certificate (server-csr.pem,) and client certificate (client1-csr.pem)? I added random Common Names for my certificate authority and client certificate, but for my server certificate it is the IP address of my server (without any protocol specification/port etc. just xxx.xxx.xx.xxx).

Furthermore, is there anything wrong with my code on the client or server-side that might get this handshake failure response?

Thanks very much for your help!

1 Like

Where do you get that error? On the server side or client side?

1 Like

If you are using an LE cert, it won't match a TLS request to an IP.

2 Likes

Hello @aaomidi,

The error is on the client-side.

Hello @rg305 ,

I'm just using standard SSL certificates generated through the commands in the referenced link, what constraints do those have in terms the name?

The URL should use the name on the cert (not the IP resolved by the name on the cert).
BAD EXAMPLE:
https://IP/zxc
GOOD EXAMPLE:
https://a.real.name/zxc

@rg305,

I will try registering the domain, adding it to the certificate, and get back to you, thanks for your help!

1 Like

@rg305 Note that the linked guide purely uses OpenSSL commands and doesn't reference Let's Encrypt anywhere. Non-Let's Encrypt certs can use IP addresses.

1 Like

If the "name" on the cert is an IP, then it would match.
If the user decided to use a self-signed cert with an IP and added it to his own trust store, then we wouldn't be having this conversation; as they would have already known how/why to do that.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.