CFSSL and Web Server

interbeing
4 min readSep 8, 2021

in this tutorial, I assume you already have CFSSL and node JS installed. I want to show how client talk to server both with certificate and client talk to server without server to authenticate client (client is browser).

to get webserver use TLS, we need a certificate, here we use cfssl to create self-signed certicicate.

create a file with name ca-config.json and ca-csr.json

#ca-config.json
{
"signing": {
"default": {
"expiry": "8760h"
},
"profiles": {
"default": {
"usages": ["signing", "key encipherment", "server auth", "client auth"],
"expiry": "8760h"
}
}
}
}
#ca-csr.json
{
"CN": "Web",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "China",
"L": "China",
"O": "Web",
"OU": "ZJ",
"ST": "Hangzhou"
}
]
}
(base) i@DESKTOP-GJNS5EN:~/cfssl$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca
2021/09/08 16:28:05 [INFO] generating a new CA key and certificate from CSR
2021/09/08 16:28:05 [INFO] generate received request
2021/09/08 16:28:05 [INFO] received CSR
2021/09/08 16:28:05 [INFO] generating key: rsa-2048
2021/09/08 16:28:05 [INFO] encoded CSR
2021/09/08 16:28:05 [INFO] signed certificate with serial number 269110615835936367907485399737534706382104948666

We use cfssl gencert to generate CA with ca-csr.json configure file. you will get private key, certificate and an CSR file. since we are the self signed CA, so we will not going to use CSR file. we will only use private key file and Certificate file. both file in PEM format.

(base) i@DESKTOP-GJNS5EN:~/cfssl$ ls -l ca*.*
-rw-r--r-- 1 i i 229 Sep 8 16:21 ca-config.json
-rw-r--r-- 1 i i 199 Sep 8 16:27 ca-csr.json
-rw------- 1 i i 1675 Sep 8 16:28 ca-key.pem
-rw-r--r-- 1 i i 989 Sep 8 16:28 ca.csr
-rw-r--r-- 1 i i 1285 Sep 8 16:28 ca.pem

now we got CA private key (ca-key.pem ) and Certificate file. (ca.pem). we will use this two file to certificate a webserver certificate. the webserver ceriticate will be used to host website.

let’s create website certificate json configuration file first. the webserver will be listing on both it’s IP and localhost. so we have to add IP address to json file. we do not use domain name, so we just use localhost instead.

(base) i@DESKTOP-GJNS5EN:~/cfssl$ cat server-config.json
{
"CN": "localhost",
"hosts": [
"127.0.0.1",
"localhost",
"172.27.92.205",
"mysite.com",
"vitaomics.com",
"*.vitaomics.com"
]
}
(base) i@DESKTOP-GJNS5EN:~/cfssl$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem server-config.json | cfssljson -bare server
2021/09/08 16:36:34 [INFO] generate received request
2021/09/08 16:36:34 [INFO] received CSR
2021/09/08 16:36:34 [INFO] generating key: ecdsa-256
2021/09/08 16:36:34 [INFO] encoded CSR
2021/09/08 16:36:34 [INFO] signed certificate with serial number 412930785902123441876528492705963507605755168990

now we use ca key and certificate to certify the server certificate based on configuration file server-config.json, so we got.

(base) i@DESKTOP-GJNS5EN:~/cfssl$ ls -l server*.*
-rw-r--r-- 1 i i 107 Sep 8 16:16 server-config.json
-rw------- 1 i i 227 Sep 8 16:36 server-key.pem
-rw-r--r-- 1 i i 428 Sep 8 16:36 server.csr
-rw-r--r-- 1 i i 1046 Sep 8 16:36 server.pem

We got server certificate server.pem and service private key server-key.pem.

we are ready to let webserver bundle with server certificate and ca certificate.

We use nodejs to create a https webserver which listing on all IP address available on that host.

const https = require('https');
const fs = require('fs');
const hostname = '0.0.0.0';
const port = 3000;
const options = {
ca: fs.readFileSync('ca.pem'),
cert: fs.readFileSync('server.pem'),
key: fs.readFileSync('server-key.pem'),
rejectUnauthorized: true,
requestCert: true,
};
const server = https.createServer(options, (req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

let’s run this webserver.

(base) i@DESKTOP-GJNS5EN:~/cfssl$ node index.js
Server running at http://0.0.0.0:3000/

then let’s create client certificate to access webserver.

(base) i@DESKTOP-GJNS5EN:~/cfssl$ cat client-config.json
{
"CN": "localhost",
"hosts": [
"127.0.0.1",
"localhost"
]
}
(base) i@DESKTOP-GJNS5EN:~/cfssl$
(base) i@DESKTOP-GJNS5EN:~/cfssl$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem client-config.json | cfssljson -bare client
2021/09/08 16:50:51 [INFO] generate received request
2021/09/08 16:50:51 [INFO] received CSR
2021/09/08 16:50:51 [INFO] generating key: ecdsa-256
2021/09/08 16:50:51 [INFO] encoded CSR
2021/09/08 16:50:51 [INFO] signed certificate with serial number 483297503968746390094185451264170512172409113701

now we also created client certificate

(base) i@DESKTOP-GJNS5EN:~/cfssl$ ls -l client*.*
-rw-r--r-- 1 i i 89 Sep 8 16:50 client-config.json
-rw------- 1 i i 227 Sep 8 16:50 client-key.pem
-rw-r--r-- 1 i i 416 Sep 8 16:50 client.csr
-rw-r--r-- 1 i i 1038 Sep 8 16:50 client.pem

now, client is ready to use TLS to access server, let’s use curl to verify

(base) i@DESKTOP-GJNS5EN:~/cfssl$ curl --cacert ca.pem --key client-key.pem --cert client.pem https://172.27.92.205:3000
Hello World(base)

— cacert ca.pem is the certificate that used to verify server certificate.

if do not want server to authenticate client, for example, allow web browser to access server. then CA.pem is not needed. we can change the node code to

const https = require('https');
const fs = require('fs');
const hostname = '0.0.0.0';
const port = 3000;
const options = {
// ca: fs.readFileSync('ca.pem'),
cert: fs.readFileSync('server.pem'),
key: fs.readFileSync('server-key.pem'),
rejectUnauthorized: true,
// requestCert: true,
};
const server = https.createServer(options, (req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

We remove ca and request Cert:true , then web server will not ask client to submit the client certificate. such allow browser to access web server

certificate can be checked via command cfssl-certinfo

(base) i@DESKTOP-GJNS5EN:~/cfssl$ cfssl-certinfo -cert server.pem
{
"subject": {
"common_name": "localhost",
"names": [
"localhost"
]
},
"issuer": {
"common_name": "AndyWang's self signed CA",
"country": "China",
"organization": "Web",
"organizational_unit": "ZJ",
"locality": "China",
"province": "Hangzhou",
"names": [
"China",
"Hangzhou",
"China",
"Web",
"ZJ",
"AndyWang's self signed CA"
]
},
"serial_number": "127095213370844276239477077208606503608787278436",
"sans": [
"localhost",
"mysite.com",
"vitaomics.com",
"*.vitaomics.com",
"127.0.0.1",
"172.27.92.205"
],
"not_before": "2021-09-09T06:55:00Z",
"not_after": "2022-09-09T06:55:00Z",
"sigalg": "SHA256WithRSA",
"authority_key_id": "59:DC:74:6E:CF:8D:50:26:70:E9:78:4A:97:1A:8E:8:2:E3:BA:35",
"subject_key_id": "B9:A4:DC:B5:D6:F7:C4:15:F2:1E:EA:77:48:29:9B:37:3C:D5:F7:B2",
"pem": "-----BEGIN CERTIFICATE-----\nMIIDGDCCAgCgAwIBAgIUFkMlf1W/Zi70N0paatKBFCkiGmQwDQYJKoZIhvcNAQEL\nBQAwcjEOMAwGA1UEBhMFQ2hpbmExETAPBgNVBAgTCEhhbmd6aG91MQ4wDAYDVQQH\nEwVDaGluYTEMMAoGA1UEChMDV2ViMQsw

--

--