TLS connectivity in NodeJS using certificate and key from Certificate store - node.js

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

Related

How get client certification info when using Hapi?

I am using the Hapi framawork in my node.js project and I'd like to get the client certificate info.
I have searched the internet looking for this info but with no luck so far.
Either I am doing a bad search or there's not an easy way to do it.
Anyways I believe that must be a way to do so.
Does anyone know how to get cert's subject, fingerprint, and etc. while using Hapi?
const tls = {
key: fs.readFileSync('../certs/xyz.key'),
cert: fs.readFileSync('../certs/xyz.crt'),
passphrase: 'xxxxxxxxxx',
ca: fs.readFileSync('../certs/rootCA.crt'),
// should request the client cert
requestCert: true,
rejectUnauthorized: true
};
console.log(req.socket.getPeerCertificate()); /* doesn't work */

How to create your own SecureContext for TLS 1.2?

I am using nodejs for TLS 1.2 requests to a server, which requires client authentication. This means, that during the inital handshake the client has sign a hash value over random values negotiated between client and server.
In nodejs you have to supply the (e.g. RSA) key or the .pfx/.p12 file along with the according passphrase with the request:
var https = require('https'), fs = require('fs')
var options = {
host: url, path: func, method: 'POST',
pfx: fs.readFileSync('mycert.pfx'),
passphrase: 'mysecret',
secureProtocol: 'TLSv1_2_method'
}
var req = https.request(options, function (res) {
...
In fact you are handing out your signature infrastructure instead of just signing a single item.
Customers don't want that, they insist in usage of Windows Certificate Store for signature, or, even better, use a hardware token (smartcard, HSM) or a remote Key Vault that administers and protocols signature creation.
For this I would need a "sign" callback when the basic TLS handshake is done.
I've seen, that nodejs is using a SecureContext object, which is created by TLS.createSecureContext(). Is it possible to replace this object by your own implementation? What is its functional interface? Has anybody already done that?
Any other ideas?

TLS what exactly does 'rejectUnauthorized' mean for me?

So, I was having an issue earlier today where my client, written in node, was barfing because the server I was connecting to used self signed certs. So, I went and added the option rejectUnauthorized: false to my tls.connect command like any unwitting developer would do.
My question is now, what the hell does this mean for me? Is my TLS connection just a vanilla TCP connection that can also possibly be a TLS connection? Is writing this as a TLS stream totally useless?
More importantly, that server, you know the one with the self-signed certs? Is my stream between here and there actually encrypted?
As described in the documentation:
rejectUnauthorized: If true, the server certificate is verified against the list of supplied CAs. An error event is emitted if verification fails; err.code contains the OpenSSL error code. Default: true.
Since you're using self-signed certificates, obviously there won't be a match with the built-in CAs, so by default the connection would be rejected because it cannot verify the server is who they say they are.
By setting rejectUnauthorized: false, you're saying "I don't care if I can't verify the server's identity." Obviously this is not a good solution as it leaves you vulnerable to MITM attacks.
A better solution for self-signed certificates is to set the appropriate ca value to your custom CA when connecting client-side. Also, make sure your host value matches that of the Common Name of the server's self-signed certificate. For example:
var socket = tls.connect({
host: 'MyTLSServer',
port: 1337,
ca: [ fs.readFileSync('CA.pem') ],
}, function() {
// Connected!
});
// ...
No matter if you use rejectUnauthorized: false or set ca, the connection is encrypted.

Using Node.js MQTT with Mosquitto PSK Encryption

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

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