DEV Community

Richard Leddy
Richard Leddy

Posted on • Edited on

Elliptic Curve TLSv1.3 for Node.js

So... This is short.

I spent a lot of time (I mean an awful amount of time) yesterday assuring myself that the few short steps in the node.js docs for TLS are as easily done with elliptic curves. After reading up on a lot of things and running my freshly secure application, I am finally satisfied that it's OK to just use the openssl ecparam and ec parameters. And, it's OK to slip them into an X509 format at the end to get files for node.js TLS connections.

Why is that something that you wouldn't just do without thinking it about? Well...

  • Problem #1

I overthink things as a habit.

  • Problem #2

I've been playing this development game for too many years. So, now I am suspicious of all documentation, all code snippets, and anyone saying he's got a quick fix.

Helpful Articles

The articles are helpful for explaining lots of things. Except, many are now quite a few years old. My! How time flies!

So, you start opening many articles returned by your favorites search engine, only to find a repetition of the first one some twenty articles back with the same code snippet from the node.js documentation.

In the end, the best thing to read was the openssl documentation. See openssl elliptics

There's lots of great articles with pretty pictures that explain elliptic curve cryptography. Some of it is beautiful. I will have to write up some review some time. Later...

Of course, if you want to go deeper into what the TLS standard is, you can always immerse yourself in the IETF documents IETF on TLSv1.3

Why this Bother?

Well... Let's say you have working processes in back-end computers. They don't all have to be HTTP/S. They can just pass messages around with their own format. For instance, I am using my own little JSON message relay stack. Find it here: message-relay-services

So, you want connections only; no other overhead.

And, yes, you want secure connections between computers that aren't even facing the outside world. Maybe within a specific cluster you can forego some security. But, these messages go between loosely connected (tiny) servers fairly close to the front of the operation. Better safe than sorry.

As a result, my implementation case even has specific client keys being configured into the servers. I have endpoint servers (those that finally do something to a table on disk or similar). They know who their client is. Of course, other than one admin desktop app, the client is most likely a middle message exchange, which serves many clients itself.

So... What are those Commands?

The ellipses once again!

Here is the two step key generation using openssl:

$ openssl ecparam -name secp384r1 -genkey -out keys/ec_key.pem

$ openssl req -new -x509 -key keys/ec_key.pem -sha256 -nodes -out keys/ec_crt.crt -days 365
Enter fullscreen mode Exit fullscreen mode

And, yes, I copied them from the docs.

The first generates the secret key. Notice that I put it in a key directory. The directory is about keeping down clutter. Put it where you need it.

Also, notice that I chose a particular curve, secp384r1. This is for 384 bits of key. There are many other curves. Check the node.js docs on how to find out what they are.

The next command generates the public key and puts it into an X509 file. The file is the cert. The req command with the X509 format guides you through putting in required fields. The fields are not magical, just where you are and who you are and a way to contact you.

That's it!

Just make sure that you do this for the server and the client.

Using the Keys in node.js

Now, this where the examples don't change. Below is code if you don't want to follow links like this one node.js tls doc.

The code is from message-relay-services. Notice that I used a configuration object. The file reading happens at initialization. It is not safe code per se. Later, I may move the file reading to an earlier point in initialization, so it can crash sooner.

You might ask, "Does this basically copy the docs?" Yes, it does, almost to the letter. Read the docs.

But, the point is:

You don't have to change old TLS code to use the EC curves. Just prepare the files differently.

This exercise has been carried out on node version v16.6.2

Server:

let base = process.cwd()
const options = {
    key: fs.readFileSync(`${base}/${this.tls_conf.server_key}`),
    cert: fs.readFileSync(`${base}/${this.tls_conf.server_cert}`),
    requestCert: true,  // using client certificate authentication
    ca: [ fs.readFileSync(`${base}/${this.tls_conf.client_cert}`) ] //client uses a self-signed certificate
};
if ( this.extended_tls_options !== false ) {
    options = Object.assign({},options,this.extended_tls_options)
}
this.connection = tls.createServer(options,((sock) => { this.onClientConnected_func(sock) }));    

Enter fullscreen mode Exit fullscreen mode

Client:

let base = process.cwd()
const tls_options = {
    // Necessary only if the server requires client certificate authentication.
    key: fs.readFileSync(`${base}/${this.tls_conf.client_key}`),
    cert: fs.readFileSync(`${base}/${this.tls_conf.client_cert}`),
    // Necessary only if the server uses a self-signed certificate.
    ca: [ fs.readFileSync(`${base}/${this.tls_conf.server_cert}`) ],
    // Necessary only if the server's cert isn't for "localhost".
    checkServerIdentity: () => { return null; },
};
if ( this.extended_tls_options !== false ) {
    tls_options = Object.assign({},tls_options,this.extended_tls_options)
}
this.socket = tls.connect(this.port, this.address, tls_options, () => {
    if ( this.socket.authorized ) {
        this._connection_handler()
    } else {
        this.socket.end()
    }
    this.writer = this.socket
});

Enter fullscreen mode Exit fullscreen mode

EXIT

If this saved you time, then maybe life has purpose.

Top comments (0)