This guide shows how to set up a bidirectional client/server authentication for plain TLS sockets.
Newer versions of openssl are stricter about certificate purposes. Use extensions accordingly.
Generate a Certificate Authority:
openssl req -new -x509 -days 9999 -keyout ca-key.pem -out ca-crt.pem
- Insert a CA Password
- Specify a CA Common Name, like 'root.localhost' or 'ca.localhost'. This MUST be different from both server and client CN.
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 vaild DNS name for this.
- For this example, do not insert the challenge password.
Sign certificate using the CA:
openssl x509 -req -days 9999 -in server-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out server-crt.pem
- insert CA Password
Verify server certificate:
openssl verify -CAfile ca-crt.pem server-crt.pem
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'. Server should not verify this, since it should not do reverse-dns lookup.
- For this example, do not insert the challenge password.
Sign certificate using the CA:
openssl x509 -req -days 9999 -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
const tls = require('tls');
const fs = require('fs');
const options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-crt.pem'),
ca: fs.readFileSync('ca-crt.pem'),
requestCert: true,
rejectUnauthorized: true
};
const server = tls.createServer(options, (socket) => {
console.log('server connected',
socket.authorized ? 'authorized' : 'unauthorized');
socket.on('error', (error) => {
console.log(error);
});
socket.write('welcome!\n');
socket.setEncoding('utf8');
socket.pipe(process.stdout);
socket.pipe(socket);
});
server.listen(8000, () => {
console.log('server bound');
});
const tls = require('tls');
const fs = require('fs');
const options = {
ca: fs.readFileSync('ca-crt.pem'),
key: fs.readFileSync('client1-key.pem'),
cert: fs.readFileSync('client1-crt.pem'),
host: 'server.localhost',
port: 8000,
rejectUnauthorized:true,
requestCert:true
};
const socket = tls.connect(options, () => {
console.log('client connected',
socket.authorized ? 'authorized' : 'unauthorized');
process.stdin.pipe(socket);
process.stdin.resume();
});
socket.setEncoding('utf8');
socket.on('data', (data) => {
console.log(data);
});
socket.on('error', (error) => {
console.log(error);
});
socket.on('end', (data) => {
console.log('Socket end event');
});
See the original post by Anders Brownworth.
Thanks to this StackOverflow answer, too (I was using same CN for CA, Server and Client and I got the DEPTH_ZERO_SELF_SIGNED_CERT
error).
Actually I don't think you can use TLS socket at all in the browser, while the fs thing is the minor issue (I was just using it to read the PEM files content, that are just plaintext files). Just curious, what is your use case here?