1

I was going through the following tutorial https://www.youtube.com/watch?v=8ptiZlO7ROs to set up an HTTPS connection, and noticed that it seems the code only makes use of server.crt and server.key (I'm not sure if server.key is the public or private key in the RSA key pair). But shouldn't two keys be needed for HTTPS? (both public and private keys?).

Altogether, the tutorial covers generation of the following files:

ca.crt
ca.key
ca.srl
client.crt
client.csr
client.key
server.crt
server.csr
server.key

But the node.js code only seems to make use of server.crt and server.key:

const express = require('express')
const fs = require('fs')
const https = require('https')
const path = require('path')

const app = express()
const directoryToServe = 'client'

const port = 3443

app.use('/', express.static(path.join(__dirname,'..',directoryToServe)))

const httpsOptions = {
    cert: fs.readFileSync(path.join(__dirname,'ssl','server.crt')),
    key: fs.readFileSync(path.join(__dirname,'ssl','server.key'))
}

https.createServer(httpsOptions,app)
    .listen(port, function() {
        console.log(`Serving the '${directoryToServe}' directory at localhost:${port}`)
    })


My understanding of HTTPS is that:

  1. The server sends the client a public key
  2. The client returns a symmetric key encrypted with the public key
  3. the server decrypts the symmetric key using the private key
  4. the client and server communicate using the symmetric key

But as the code above uses only one key server.key (which I assume is the public key?), how does the HTTPS communication work since I assume both assymetric keys should be needed (public and private) to negotiate the symmetric key exchange?

Here is the code to generate the keys/certificates:

#!/bin/bash

# set values for certificate DNs
# note: CN is set to different values in the sections below
ORG="000_Test_Certificates"

# set values that the commands will share
VALID_DAYS=360
CA_KEY=ca.key
CA_CERT=ca.crt
CLIENT_KEY=client.key
CLIENT_CERT=client.crt
CLIENT_CSR=client.csr
CLIENT_P12=client.p12
SERVER_KEY=server.key
SERVER_CERT=server.crt
SERVER_CSR=server.csr
KEY_BITS=2048

echo
echo "Create CA certificate..."
CN="Test CA"
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:$KEY_BITS -out $CA_KEY
openssl req -new -x509 -days $VALID_DAYS -key $CA_KEY -subj "//CN=$CN\O=$ORG" -out $CA_CERT
echo "Done."

echo
echo "Creating Server certificate..."
CN="localhost"
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:$KEY_BITS -out $SERVER_KEY
openssl req -new -key $SERVER_KEY -subj "//CN=$CN\O=$ORG" -out $SERVER_CSR
openssl x509 -days $VALID_DAYS -req -in $SERVER_CSR -CAcreateserial -CA $CA_CERT -CAkey $CA_KEY -out $SERVER_CERT
echo "Done."

echo
echo "Creating Client certificate..."
CN="Test User 1"
USER_ID="testuser1"
P12_PASSWORD=
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:$KEY_BITS -out $CLIENT_KEY
openssl req -new -key $CLIENT_KEY -subj "//CN=$CN\O=$ORG\UID=$USER_ID" -out $CLIENT_CSR
openssl x509 -days $VALID_DAYS -req -in $CLIENT_CSR -CAcreateserial -CA $CA_CERT -CAkey $CA_KEY -out $CLIENT_CERT
openssl pkcs12 -in $CLIENT_CERT -inkey $CLIENT_KEY -export -password pass:$P12_PASSWORD -out $CLIENT_P12
echo "Done."

echo
echo "----- Don't forget to open your browser and install your $CA_CERT and $CLIENT_P12 certificates -----"
echo

Thank you in advance!

6
  • 1
    Certificates contain public keys, among other information.
    – Ghedipunk
    Oct 30 '19 at 21:49
  • Thank you. So the certificate contains the public key, and the server.key contains the private key? I've appended the key generation code above in the OP. Oct 30 '19 at 21:54
  • 1
    The private key is never transmitted by the server, only the public, thus it's called private in the first place.
    – game0ver
    Oct 30 '19 at 21:58
  • Thank you. I realize that. But I would assume that the location of the private key needs to be made known to the server, and I didn't any reference in the code to the private key (unless as @Ghedipunk may be suggesting server.key is the private key, and the public key is contained in server.crt). Alternatively, if that's not the case, how would the server know where to fetch its private key? (which is really my original question) Oct 30 '19 at 22:15
  • 1
    I looked up the options parameter for the https.createServer() method. That says it accepts any options from tls.createServer(), tls.createSecureContext() and http.createServer(). Digging a bit more, it's tls.createSecureContext() that asks for the key and cert parameters: nodejs.org/api/tls.html#tls_tls_createsecurecontext_options . This will answer your question definitively.
    – Ghedipunk
    Oct 30 '19 at 22:22
2

The certificate (which is sent from the server to the client) contains the public key (along with necessary information for the recipient to verify the key's authenticity). The other file (created by the genpkey operation) contains the private key (and enough information to produce the public key), and is never sent anywhere.

Run man genpkey to see more info about the genpkey operation.

0

Meta: Two less important points, not really an answer, but much too long for comments.

My understanding of HTTPS is that:
1. The server sends the client a public key
2. The client returns a symmetric key encrypted with the public key
3. the server decrypts the symmetric key using the private key
4. the client and server communicate using the symmetric key

This is partly wrong and mostly out of date. (Which is why you will find it on billions of blogs, but that's a different issue.) As CBHacking already noted, the server sends a certificate which contains its public key plus its identity, a validity period, some other metadata we can ignore for now, and a signature by a Certificate Authority (CA). Before using the publickey, the client verifies that the certificate is signed by a CA which the client trusts (and which CAs it trusts is a whole topic in itself -- google Public Key Infrastructure aka PKI and/or see many existing Stack Qs), is in the validity period and not revoked (another barrel of worms), has certain other metadata correct, and is for the correct server -- for HTTPS, the certificate matches the domainname or (less often) IPaddress from the URL the client is accessing.

For the original 'RSA' key-exchange method mostly used in SSL3 and TLS 1.0 through 1.2 in the 1990s and 2000s, the client sends a secret, called the premaster secret or PMS, which is not actually a key, encrypted by the server publickey, and the server decrypts it; both parties then use this PMS in a process that derives several symmetric keys used to subsequently communicate. The details of this process are in the RFCs and several existing Qs.

SSL3 (before it was broken and dropped) and TLS1.0-1.2 also support other key-exchange methods, in particular several variants of 'Diffie-Hellman ephemeral' (DHE or DH-E) which provide a feature called 'forward secrecy' (also 'perfect forward secrecy' or PFS) meaning that, unlike the RSA method, even if the server privatekey is subsequently compromised it cannot be used to decrypt sessions recorded in the past. In this decade DHE started to become more popular, and it took off after Snowden made most of the world aware of large-scale government monitoring of Internet traffic. TLS using DHE key-exchange is, roughly:

  1. server sends certificate with its (static/certified) publickey, plus a newly-chosen ephemeral publickey (including parameters) signed using its privatekey

  2. after validating the certificate as above, client also verifies the signature on the server's ephemeral key, chooses its own ephemeral key using the same parameters (group for classic, curve for EC), and sends that

  3. each party combines their own ephemeral privatekey with the other party's ephemeral publickey to produce a shared secret (PMS)

  4. each party uses PMS to derive the several symmetric keys used to communicate

  5. each party destroys its ephemeral (private)key; this is what prevents any future compromise

TLS1.3, published last year and now being phased into use, drops RSA key-exchange completely and allows only DHE key-exchange. (It also makes a number of other security improvements not relevant here, but covered by other Qs.)

[from comment] But just curious, what is the purpose of client.key in the tutorial, if server.key and server.crt are all that are needed?

In addition to normally using a certificate and matching privatekey to authenticate the server (almost always; technically it is possible to omit server authentication but implementations and users almost never do), TLS has the capability to authenticate the client in the same way. This is often called 'two-way' (or '2-way') or 'mutual' authentication, since it combines server and client auth. This uses a keypair and matching certificate (chain) for the client separate and independent from the server's.

Client auth is rarely used for the WWW, because the UI generally required in a browser to do it requires people to understand words of more than two syllables often in more than one sentence, and 99.9999% of web/browser users apparently don't, based on observation of their behavior. It is sometimes used for websites internal to a business or organization accessed only by employees who can be trained and retrained as many hours as needed; in particular this often uses a client key-and-cert stored on a smartcard issued to the employee which prevents unauthorized copying of the key (either accidental or intentional) and can usually be taken back or destroyed (and the cert revoked) when the employee leaves. However, HTTPS is not used only for WWW, but also as a transport for some applications, plus TLS can be used to carry protocols other than HTTPS. For some of these other cases, client auth is used, and apparently that tutorial wants to allow for this possibility.

The difference is that in a real situation, all these parties would be separate. The server should generate its privatekey and obtain a cert for the matching publickey (and the server's identity) from a CA, with only the server having access to the server privatekey and only the CA having access to the CA privatekey. (And a proper CA will protect that CA privatekey zealously, with special hardware, dedicated and trained personnel, round-the-clock guards, offsite surveillance and armed response, etc. They won't be using openssl x509 -req.) Similarly the client, if applicable, should separately and independently generate its privatekey and obtain a cert from a CA -- possibly a different CA than the server used, but again a CA that keeps its privatekey private.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.