How to create your own SecureContext for TLS 1.2? - node.js

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?

Related

SocketIO throws net::ERR_CERT_AUTHORITY_INVALID on self signed certificate

I am using socket io on client:
const socket = require('socket.io-client')('https://localhost:4200', {secure: true, rejectUnauthorized: false})
And on server:
let https = require('https')
let fs = require('fs')
let options = {
key: fs.readFileSync('cert/my.net.key'),
cert: fs.readFileSync('cert/my.net.cert'),
requestCert: false,
rejectUnauthorized: false,
};
const server = https.createServer(options, require('express')())
const io = require('socket.io')(server)
All services are started normally, but on client I am getting polling-xhr.js:263 GET https://localhost:4200/socket.io/?EIO=3&transport=polling&t=MPa6ZuL net::ERR_CERT_AUTHORITY_INVALID
Why? Whats is wrong?
Browsers don't like self-signed certificates for security reasons.
To get around this in your development environment, I see three options:
Use a certificate issued by a certification unit.
It could be something free, like https://letsencrypt.org/.
Create your server dynamically, based on the development environment, not to include certificates and work directly with HTTP and WS (and not HTTPS and WSS).
Change the configuration of your browser used in development so that it accepts self-signed certificates.
For Chrome, for example, just enable the Allow invalid certificates for resources loaded from localhost. (chrome://flags/#allow-insecure-localhost) setting.
But remember that you will not be able to use self-signed certificates in production environments.

NodeJS check validity of SSL certification other host

I want to check from my server, if the SSL certificate is valid on another server/domain. Is there any way how I can check this?
I think I need to use the https API from NodeJS, but I'm not sure.
When you make an SSL request from NodeJS using its https library, it will take care of the job of verifying the validity of the server it's contacting.
From NodeJS doc:
rejectUnauthorized: If true, the server certificate is verified
against the list of supplied CAs. An 'error' event is emitted if
verification fails. Verification happens at the connection level,
before the HTTP request is sent. Default true
Further more, you can assert the res.socket.authorized attribute in the response:
var https = require('https');
var options = {
host: 'google.com',
method: 'get',
path: '/'
};
var req = https.request(options,
function (res) {
console.log('certificate authorized:' + res.socket.authorized);
});
req.end();
You can also use res.socket.getPeerCertificate() to get detailed information on the server certificate.

Self signed cert NodeJS rejectUnauthorized

I created a structure certificates to authenticate client-> server, allowing only certificates recognized by the CA using this step by step: https://jamielinux.com/docs/openssl-certificate-authority/create-the-root-pair.html
I checked the authority with openssl, and it returns to me OK the certificate server and client, with the same CA. But by setting the parameter rejectUnauthorized to true on the server, the client can not connect.
Is there any extra parameter should I set up to allow authentication by a certificate that I generated?
---- Edit
On the client side I get the following error: ""ECONNRESET" socket hang up"
I spent a long time digging into a similar issue, and I wrote up this to talk about how to dig into various OpenSSL issues with node.js: http://www.thedreaming.org/2016/09/27/nodejs-ssl/
The short answer, though, is if you need to pass the ca parameter when creating you client connection. If you have the self-signed certificate stored in cert.pem, then the client code looks something like:
var https = require('https');
var fs = require('fs');
var certificate = fs.readFileSync('cert.pem');
var options = {
host: serverHost,
port: 443,
path: '/',
ca: [certificate]
};
https.request(options, function(res) {
res.pipe(process.stdout);
}).end();

How can I know that a HTTPS endpoint receiving a TLS request from my node.js is using a specified SSL certificate?

I have an endpoint (in any language, let's say Python) that exposes some service as HTTPS using a certificate issued by any widely known and trusted CA, that is
probably included in virtually any browser in the world.
The easiest part is that I can issue TLS requests against this endpoint using Node.js with no further problems.
For security reasons, I would like to check that every time my Node.js issues a TLS request against this HTTPS endpoint, I want to make sure that the certificate being used, is the certificate that I trust, and the one that was requested by my company.
What is the best way to accomplish that?
It sounds like the answer at How to get SSL certificate information using node.js? would be suitable for your needs.
You can use the following code to get your endpoint's certificate then check its fingerprint or hash against what you expect.
var https = require('https');
var options = {
host: 'google.com',
port: 443,
method: 'GET'
};
var req = https.request(options, function(res) {
console.log(res.connection.getPeerCertificate());
});
req.end();

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