Category: OpenSSL
Creating a Certificate Signing Request (CSR)
For a recent project we needed a signed certificate for an HTTPS server, and to import into the AWS Certificate Manager. This article is for you if you need to submit a CSR to an administrator or a signing authority (like DigiCert) who will sign your private key with their CA.
First you’ll need to create a private key, and then create your CSR from that, adding the necessary information.
As it turns out, there is a single OpenSSL command that will do all of that. The private key it creates is also suitable for import to the AWS Certificate Manager.
Start with the following information on hand:
- Country Code
- State/Province
- City
- Company Name (no abbreviations)
- Fully Qualified Domain Name (FQDN), e.g. intown.biz, or www.intown.biz. You can only use one FDQN, but you can use a wildcard, e.g. *.intown.biz.
- an administrator email address
Issue the following Command:
openssl req -new -nodes -out intown-biz.key.pem -keyout intown-biz.csr.pem
Sample results:
Generating a 2048 bit RSA private key ..............+++ ...............+++ writing new private key to 'intown-biz.key.pem' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:Maryland Locality Name (eg, city) []:Bethesda Organization Name (eg, company) [Internet Widgits Pty Ltd]:The In Town Company Organizational Unit Name (eg, section) []:consulting Common Name (e.g. server FQDN or YOUR name) []:intown.biz Email Address []:administrator@intown.biz Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
Note that I skipped the optional parts, including the password.
This command is really doing three things:
- Creating a private key (and calling it intown-biz.key.pem)
- Prompting you for the information you need, for a signing certificate.
- Creating the signing certificate and calling it intown-biz.crt.pem)
Double check your work
Run this command to view the decoded value of your CSR.
openssl req -in intown-biz.crt.pem -noout -text
Submit the CSR
The CSR goes to your administrator, or a signing authority. The cool thing about the CSR is that once it is used to create your public key (or “certificate”), the key pair will be “signed” without having to hand over the actual private key to the signing authority.
Hold on to the private key! That’s the only copy you get. Make sure to follow your company’s policy for managing private keys.
When the signing request is processed, you should receive:
- The public certificate or “certificate body” which you can give out
- An intermediate CA or “Certificate Chain” which helps the server to locate the signing CA.
At this point you’re pretty much done with the CSR. With the pubic cert, private cert and intermediate ca in hand, you’re good to go!
If you’re using the AWS Certificate Manager, simply paste the text of these into the appropriate boxes.
Use SSL Client Authentication with node.js and Express
For a recent engagement, I was asked to set up client authentication for a node.js/express app. With this type of authentication, an SSL certificate is required to access the site.
To get the most out this article, download and run the accompanying this demonstration app. It contains scripts to create the necessary certificates, similar to what you see here. These scripts and this article use openssl, which can be found in Linux or the Git for Windows bash shell.
For these purposes you don’t need to understand everything about certs, but here are some basics:
Create a signing key (CA)
Everything starts with a signing key, which can be created with a command similar to this:
openssl req -new -x509 -days 365 -keyout ca-key.pem -out ca-crt.pem
The resulting cert and key can then be used to sign other certificates. Creating a self signed cert is similar.
Create a signed certificate
Creating a signed certificate is a multi-step process. First you’ll need a signing key, as generated above.
export CERT=server export CA_NAME=ca #generating private key for server openssl genrsa -out $CERT-key.pem 4096 #generate a signing request openssl req -new -sha256 -config $CERT.cnf -key $CERT-key.pem -out $CERT-csr.pem #perform the signing openssl x509 -req -days 365 -in $CERT-csr.pem -CA $CA_NAME-crt.pem -CAkey $CA_NAME-key.pem -CAcreateserial -out $CERT-crt.pem
The initial command (genrsa) generates public and private keys. The third command’s final product, server-crt.pem, is used along with the private key to run ssh in express.
Set up https
To start, your express app must be running https, so you’ll need to have (or generate) a public and private key for that. Here is an example of how to set that up:
var express = require('express'); var fs = require('fs'); var https = require('https'); var app = express(); var options = { key: fs.readFileSync('certs/server-key.pem'), cert: fs.readFileSync('certs/server-crt.pem'), }; app.use(function (req, res, next) { res.writeHead(200); res.end("hello world\n"); next(); }); var listener = https.createServer(options, app).listen(4433, function () { console.log('Express HTTPS server listening on port ' + listener.address().port); });
Test https
$ node app Express HTTPS server listening on port 4433
Test using curl in another window. Note that we use -k to bypass certificate validation.
$ curl https://127.0.0.1:4433 -k hello world
Set up client authentication
Once you have ssh cooperating, you can add client auth. requestCert will require the client (e.g. curl, browser) to send a cert. rejectUnauthorized=false allows us to handle validation ourselves.
If you’re using a browser,
You’ll notice that now we have the client auth certificate. If this was created as a CA, then ANY certificate which is signed by this one will validate.
var options = { key: fs.readFileSync('certs/server-key.pem'), cert: fs.readFileSync('certs/server-crt.pem'), ca: fs.readFileSync('certs/ca.pem'), #client auth ca OR cert requestCert: true, #new rejectUnauthorized: false #new };
Here’s how we handle the validation:
app.use(function (req, res, next) { if (!req.client.authorized) { return res.status(401).send('User is not authorized'); } #examine the cert itself, and even validate based on that! var cert = req.socket.getPeerCertificate(); if (cert.subject) { console.log(cert.subject.CN); } next(); });
Test client authentication
# Access the site using the client certificate created above. curl -v -s -k --key certs/client1-key.pem --cert certs/client1-crt.pem https://localhost:4433
Test client authentication with a browser
- Add the client pfx file to your certificate store. If you’re using a newly created CA, you might need to add its pfx as well. If you’re not presented with a dialog box in step 3, this is likely the problem.
- Browse to https://localhost:4433.
- You should be presented with a dialog box. Choose the client certificate.
- Note that if you change the certificate, you will need to completely close Chrome (and possibly need to kill the task) in order to see the dialog again.
Certificates CAN be revoked. This is not covered in this article, but see the appropriate section here.
Other articles you might like:
HTTPS Authorized Certs with Node.js
How To Create an SSH CA to Validate Hosts and Clients with Ubuntu