Client-side websocket certificate in NodeJS - node.js

I have a NodeJS websocket client app, using ws https://www.npmjs.com/package/ws - this NodeJS app connects as a client to a websocket server.
I can use HTTPS by specifying wss:// as the protocol.
How can I make the TLS connection use a client certificate for authentication?
i.e. the websocket client should use a certificate to prove its identity to the server.

I found:
it('connects to secure websocket server with client side certificate', function(done) {
const server = https.createServer({
cert: fs.readFileSync('test/fixtures/certificate.pem'),
ca: [fs.readFileSync('test/fixtures/ca1-cert.pem')],
key: fs.readFileSync('test/fixtures/key.pem'),
requestCert: true
});
let success = false;
const wss = new WebSocket.Server({
verifyClient: (info) => {
success = !!info.req.client.authorized;
return true;
},
server
});
wss.on('connection', () => {
assert.ok(success);
server.close(done);
wss.close();
});
server.listen(0, () => {
const ws = new WebSocket(`wss://localhost:${server.address().port}`, {
cert: fs.readFileSync('test/fixtures/agent1-cert.pem'),
key: fs.readFileSync('test/fixtures/agent1-key.pem'),
rejectUnauthorized: false
});
});
});
on https://github.com/websockets/ws/blob/14d9088391ac4495d04e64d76c3b83d4e75f80e2/test/websocket.test.js

Related

websocket connection to 'wss://url_from_aws_domain/socket.io/?EIO=4&transport=websocket' failed?

I have implemented a real time notification using socket.io(https://cdnjs.com/libraries/socket.io) on my local and it is working fine. But when I try to implement it on the live server deployed at AWS domain, the frontend that is establishing socket connection to server got error, websocket is unable to connect on the server throwing error:
websocket connection to
'wss://url_from_aws_server/socket.io/?EIO=4&transport=websocket'
failed?
Front-end Side(notification sender)
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.min.js"></script>
<script>
var socket = io('https://app.url_from_aws_server.com:4492', { transports: ['websocket', 'polling', 'flashsocket'] });
$(document).on("click",'#confirmSlot',function(){
socket.emit("notify_platform", {
"shop": locationName,
"therapist": therapistName,
"date": therapistDate,
"time": therapistTime,
});
}
</script>
Server Side
const express = require('express');
const app = express();
...
const sslOptions = {
key: fs.readFileSync(path.join(__dirname, '../ssl/key.pem')),
cert: fs.readFileSync(path.join(__dirname, '../ssl/cert.pem')),
requestCert: false,
rejectUnauthorized: false,
};
const https = require('https').createServer(sslOptions, app);
const io = require('socket.io')(https, {
cors: {
origin: 'https://app.url_from_aws_server.com:4492',
methods: ['GET', 'POST'],
},
});
...
https.listen(process.env.SSL_PORT, () => {
console.log(
`HTTPS Server started at ${process.env.SSL_PORT}, Database - ${process.env.MONGODB_URI}`
);
io.on('connection', function(socket){
console.log('Auth value:' + socket.id);
socket.on('notify_platform', function(details){
socket.broadcast.emit('notify_platform', details);
})
})
});

NodeJS - Establish a Connection WebSocket with protocol HTTPS

I have a problem with an HTTPS and a WebSocket connection (I use Node.JS as a server).
I have generated SSL certificates with OpenSSL and I imported them to the server with the following code:
const https = require('https');
var app = express();
...
const WebSocket = require('ws');
...
var serverHttps = https.createServer({
key: fs.readFileSync(path.join(pathCertificati, 'key.pem')),
cert: fs.readFileSync(path.join(pathCertificati, 'cert.pem'))
}, app).listen(3000, () => {
console.log('In ascolto sulla porta 3000 HTTPS.')
})
const wss = new WebSocket.Server({
server: serverHttps,
adress: '192.168.12.40',
port: 9000
});
With this code I should have a WebSocket handling an HTTPS connection, correct?
Client side, I have the following code:
socket = new WebSocket("wss://192.168.12.40:9000");
socket.onopen = function ()...
socket.onclose = function ()...
socket.onerror = function (error) {
console.log("Errore nella connessione!");
console.log(error);
}
When I load the page using the address: https://192.168.12.40:3000 and the above code is executed, the error message appears:
WebSocketScript.js:26 WebSocket connection to 'wss://192.168.12.40:9000/' failed: Error in connection establishment: net::ERR_SSL_PROTOCOL_ERROR
Do you have any ideas to establish the WebSocket Connection on an HTTPS page?
Thanks a lot.
I have found the solution to the problem. The solution is:
Server-side code:
var serverHttps = https.createServer({
key: fs.readFileSync(path.join(pathCertificati, 'key.pem')),
cert: fs.readFileSync(path.join(pathCertificati, 'cert.pem'))
}, app).listen(3000, () => {
// Messaggio di attivazione del server in ascolto sulla porta 3000
console.log('Listening ' + 3000 + ' HTTPS.');
})
serverHttps.on('upgrade', function upgrade(request, socket, head) {
const pathname = url.parse(request.url).pathname;
if (pathname === "/") {
wss.handleUpgrade(request, socket, head, function done(ws) {
wss.emit('connection', ws, request);
});
}
});
// Create WebSocket
const wss = new WebSocket.Server({ noServer: true });
wss.on('error', function () {...}
wss.on('connection', function (ws) {...}
Client side:
socket = new WebSocket("wss://192.168.12.40:3000);
This is the same address used on the server side and it fire the event "serverHttps.on('upgrade'"
Thanks a log...

How to use nodejs ws module on secure site

I'm using the following code to connect to WebSockets on a node js app for HTTP, but to doesn't work in HTTPS. What do I need to change? Is there a secure version of the node js ws module?
const WebSocket = require('ws');
webSocketServer.on('connection', (webSocket) => {
webSocket.on('message', (message) => {
...
});
});
Pays to read the nodejs ws module documentation sometimes:
let webSocketServer;
if (!local) {
const server = https.createServer({
cert: fs.readFileSync("/... server.crt"),
key: fs.readFileSync("/... server.key")
});
webSocketServer= new WebSocket.Server({ server });
server.listen(8080);
}
else webSocketServer = new WebSocket.Server({ port:8080 });
On the client-side, you need to call it with wss:// instead of ws://

Nodejs, TLS and only allow certain client certificates

I'm running NodeJS with TLS and have created a server like so:
const tls = require('tls');
const fs = require('fs');
const options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
rejectUnauthorized: true,
requestCert: true,
ca: [ fs.readFileSync('clientX-cert.pem') ]
};
const server = tls.createServer(options, (socket) => {
console.log('server connected', socket.authorized ? 'authorized' : 'unauthorized');
socket.on('data', function (data) {
socket.write(data);
});
});
server.listen(5000);
I'm trying to only approve client with a specific client certificate clientX-cert.pem, but it seems to fail as my client is getting an Error: socket hang up at his end.
When not having requestCert it does work, but then everyone is allowed with a TLS certificate.
Have I misunderstood the rejectUnauthorized: true, requestCert: true and ca: options?

a specific case: UNABLE_TO_VERIFY_LEAF_SIGNATURE

I've been trying to do a test about TLS connection between server and client. (SSL-Client Authentication)
I have a self-signed key pair.
If I try to connect my API server using tls.connect(), my connection seems as unauthorized and authorizationError value is UNABLE_TO_VERIFY_LEAF_SIGNATURE as shown the below screenshot.
However, if I try to connect https://api.twitter.com instead of https://hellolarim.club then there is no error and authorization value is true. Also when I trying to Twitter I don't have to use rejectUnauthorized: false parameter.
I added the below server.jscodes too.
Question: I'm wondering that why I cannot connect as authorized: true ?
I have to implement SSL-Client Authentication to my API server.
The attempt to connect to my API server
client.js
var tls = require('tls');
var fs = require('fs');
var options = {
key: fs.readFileSync('client-private-key.pem'),
cert: fs.readFileSync('client-certificate.pem'),
rejectUnauthorized: false,
};
{
var cleartextStream = tls.connect(443, 'www.hellolarim.club', options, function() {
console.log('\nclient connected', cleartextStream.authorized ? 'authorized' : 'unauthorized');
if(!cleartextStream.authorized) {
console.log("authorizationError: " + cleartextStream.authorizationError);
}
process.stdin.resume();
process.stdin.pipe(cleartextStream);
});
cleartextStream.setEncoding('utf8');
cleartextStream.on('data', function(data) {
console.log("\n" + data);
});
cleartextStream.on('end', function() {
server.close();
});
The attempt to connect to Twitter API server
Server.js
var tls = require('tls');
var fs = require('fs');
var options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
requestCert: true,
rejectUnauthorized: true,
ca: [ fs.readFileSync('../client/client-certificate.pem') ]
};
var server = tls.createServer(options, function(cleartextStream) {
console.log(cleartextStream.getPeerCertificate());
console.log('server connected',
cleartextStream.authorized ? 'authorized' : 'unauthorized');
cleartextStream.write("Hello from server to client!\n");
cleartextStream.setEncoding('utf8');
cleartextStream.pipe(cleartextStream);
});
server.listen(443, function() {
console.log('server bound');
});
The problem is on client side.
Client cannot recognize server's certificate despite the certificate had been gotten from a commercial CA because there is no ca parameter on client options!
And if we have a lot of root certificates we can install them by transforming a string array like the below or use node-ssl-root-cas.
var CAcerts = [
// A Root CA cert.
"-----BEGIN CERTIFICATE-----\n" +
"MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE\n" +
"BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG\n" +
"tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW\n" +
"sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp\n" +
"7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I\n" +
"LaZRfyHBNVOFBkpdn627G190\n" +
"-----END CERTIFICATE-----\n",
// Another Root CA cert.
"-----BEGIN CERTIFICATE-----\n" +
"MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE\n" +
"BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG\n" +
"tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW\n" +
"sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp\n" +
"7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I\n" +
"LaZRfyHBNVOFBkpdn627G190\n" +
"-----END CERTIFICATE-----\n"
];
var options = {
key: fs.readFileSync('client-private-key.pem'),
cert: fs.readFileSync('client-certificate.pem'),
ca: CAcerts,
requestCert: true,
rejectUnauthorized: true,
};

Resources