Using Node.js MQTT with Mosquitto PSK Encryption - node.js

I'm working on developing a solution using MQTT to send/receive data to embedded systems. For a broker I'm using Mosquitto. For the client I'm using Node.js MQTT.
I need to encrypt the data and I'd like to use the pre-shared key option in mosquitto to accomplish this however, I can't seem to find anything built into the Node.js MQTT package to do this. Is this possible?
From the Mosquitto configuration docs:
When using pre-shared-key based encryption through the psk_hint and
psk_file options, the client must provide a valid identity and key in
order to connect to the broker before any MQTT communication takes
place. If use_identity_as_username is true, the PSK identity is used
instead of the MQTT username for access control purposes. If
use_identity_as_username is false, the client may still authenticate
using the MQTT username/password if using the password_file option.

Node does support TLS-PSK now, but PSK ciphers are disabled by default.
I finally could connect with the following options:
const client = mqtt.connect('mqtts://localhost:8883', {
pskCallback: (hint) => {
console.log('psk_hint configured in mosquitto.conf', hint);
return {
psk: Buffer.from('1234', 'hex'),
identity: 'DeviceId',
};
},
ciphers: crypto.constants.defaultCipherList.replace(':!PSK', ''),
});
psk_file must include the line DeviceId:1234 in this example.
My main problem was, that configuring a custom ciphers list must include HIGH for whatever reason. It even works with ciphers: 'HIGH'

It appears the MQTT package hands off to Node's TLS capabilities and Node doesn't support TLS PSK.
Preshared keys (TLS-PSK-WITH-AES-256-CBC-SHA) with node.js server

Related

Connecting to Oracle with SSL

I am trying to connect to Oracle DB 12c from an electron (nodeJs) application.
I am creating the connection by passing parameters, using the knex library, like this:
knex({
client: 'oracledb',
connection: {
host: hostItems + ':' + connection.Port,
user: connection.UserName,
password: connection.Password,
database: connection.DatabaseName
};
});
In knex the connection parameter is the same as node-oracledb which is used internally.
It works for non-ssl connections like using port 1521, but not for 2484 the standard oracle SSL port. I have the CA certs with me, but I dont know how to pass them.
For the SSL port I get 12547: TNS Lost Contact which sounds about right as it cannot establish SSL connection.
I am trying to figure out how to use SSL with node-oracledb.
The official node-oracledb documentation contains a section describing how to properly configure SSL/TLS.
Since the NodeJS application will be acting as the client in this communication scenario, it must provide the certificates during the handshake (as a browser would for an example).

node.js mqtt client using TLS

I am trying to implement a node.js mqtt client with TLS using the package below;
https://www.npmjs.com/package/mqtt#client
The code for running mqtt client without TLS is as follows;
var mqtt = require('mqtt')
var client = mqtt.connect('mqtt://test.mosquitto.org')
client.on('connect', function () {
client.subscribe('presence')
client.publish('presence', 'Hello mqtt')
})
client.on('message', function (topic, message) {
// message is Buffer
console.log(message.toString())
client.end()
})
How should the above code be modified to use TLS on the mqtt client?
The mosca MQTT broker was run as a stand-alone using the command below;
mosca --key ./tls-key.pem --cert ./tls-cert.pem --http-port 3000 --http-bundle --http-static ./ | pino
Should be enough to change the protocol part of the URL to mqtts://
mqtts://test.mosquitto.org.
Self-signed certificates
You can pass the following option to the connect function when using self-signed certificates (for testing purposes only):
mqtt.connect('mqtts://test.mosquitto.org', {
rejectUnauthorized: false
});
You need to provide the mqtt.connect() function with an options object which includes the CA certificate to use to verify the connection.
The options object needs to include a ca key that points to the certificate used to sign the brokers certificate. As it looks like your using a self signed certificate this will be the same one used by the broker.
The ca key is described here
Or you can allow any certificate with the rejectUnauthorized key as mentioned in #notion's answer. But that makes it impossible to detect if somebody is impersonating your broker

TLS connectivity in NodeJS using certificate and key from Certificate store

I have implemented TLS connectivity through MQTT as shown below.
mqttOptions = {
clientId: '100',
key: fs.readFileSync('test/certs/client.key'),
cert: fs.readFileSync('test/certs/client.crt'),
ca: fs.readFileSync('test/certs/ca.crt'),
secureProtocol: 'TLSv1_method',
rejectUnauthorized: false,
protocolId: 'MQIsdp',
protocolVersion: 3,
passphrase: 'edgenode',
keepAlive: 1000,
clean: false,
reconnectPeriod: '1000',
will: willMessage
};
var client = mqtt.connect(tls://localhost:8883, mqttOptions);
Here I'm passing in the client key, certificate and the CA certificate. Instead I need to connect using information from Windows certificate store. Assuming I have installed the client certificate in Windows Certificate Store, how can I read the private key from it using nodeJS and establish connectivity? Please advice.
Access to the Windows Keystore is via the MSCAPI.
I can't see any NodeJS wrappers for this API on npm but even if there were you would have to modify the mqtt library to work with it as it won't actually give you access to the private client keys, but instead you pass in data to be signed/encrypted using that key and it gives you back the signed/encrypted data like a hardware crypto device.
If you really need to use the Windows keystore I would suggest porting the client app to something like C, C# or Java as there are MSCAPI libraries for these that present the keys/certs using the language standard APIs

run logstash-forwarder in untrusted network-environment

I want to figure out a safe way to run logstash-forwarder respectively logstash with the lumberjack-input in an untrusted network-environment.
As far as I understand, the SSL-certificate ensures an encrypted connection between client and server und authenticates the server for the client (as in "ok, I know this server is the real logging-server"). How can I authenticate the client for the server (as in "ok, I know this client trying to send me events is one of my machines, not someone else")?
SSL certificates can work in bidirectional way. They can be used to authenticate the server ("ok, this server is the real logging-server") and also the other way around ("ok, I know this client is one of my machines"). For the second case you need to use client certificates.
Although Logstash Forwarder allows to configure a client certificate, logstash's lumberjack input does not support client certs. There is an open github issue regarding this feature.
To overcome this dilemma you can use an alternative log client and logstash's TCP input which supports client certs. The input will look like this:
input {
tcp {
port => 9999
ssl_cert => "/path/to/server.crt"
ssl_key => "/path/to/server.key"
ssl_cacert => "/path/to/ca.crt"
ssl_enable => true
ssl_verify => true
}
}
On the client side you can use several tools. I personally do this with NXLog. A proper NXLog output config would look like this:
<Output logstash>
Module om_ssl
Host yourhost
Port 9999
CAFile %CERTDIR%/ca.crt
CertFile %CERTDIR%/client.crt
CertKeyFile %CERTDIR%/client.key
</Output>
Unfortunately this is just a workaround with another software but I'm afraid there is no native lumberjack solution.

How to verify that a connection is actually TLS secured?

I have created a TLS server and an appropriate TLS client in Node.js. Obviously they both work with each other, but I would like to verify it.
Basically, I think of something such as inspecting the connection, or manually connecting to the server and inspecting what it sends, or something like that ...
The relevant code of the server is:
var tlsOptions = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('server.pem')
};
tls.createServer(tlsOptions, function (tlsConnection) {
var d = dnode({
// [...]
});
tlsConnection.pipe(d).pipe(tlsConnection);
}).listen(3000);
The appropriate client code is:
var d = dnode();
d.on('remote', function (remote) {
// [...]
});
var tlsConnection = tls.connect({
host: '192.168.178.31',
port: 3000
});
tlsConnection.pipe(d).pipe(tlsConnection);
How could I do that?
Wireshark will tell you if the data is TLS encrypted, but it will not tell you if the connection is actually secure against Man-in-the-Middle attacks. For this, you need to test if your client refuses to connect to a server that provides a certificate not signed by a trusted CA, a certificate only valid for a different host name, a certificate not valid anymore, a revoked certificate, ...
If your server.pem is not a certificate from a real/trusted CA, and your client doesn't refuse to connect to the server (and you didn't explicitly provide server.pem to the client), then your client is very probably insecure. Given that you are connecting to an IP, not a host name, no trusted CA should have issued a certificate for it, so I assume you use a selfsigned one and are vulnerable. You probably need to specify rejectUnauthorized when connect()ing. (Rant: As this is a pretty common mistake, I think it is extremely irresponsible to make no verification the default.)
Basically, I think of something such as inspecting the connection, or manually connecting to the server and inspecting what it sends, or something like that ...
You can use tools such as Wireshark to see the data they are transmitting.

Resources