Before we start, we want to make sure you have OpenSSL installed. You can check by running the command:
$ openssl version
If that returns you a version, you're good. If not, then install OpenSSL using
$ sudo apt update && sudo install openssl
Now that OpenSSL is installed, we want to first generate a CA certificate. This is so that we can validate the client certificates against this CA to make sure they're valid. It'll ask you a series of questions via a wizard. In my case, I'm doing AES256 encryption, you can do whichever else you feel like; though AES256 is the most common on to use, unless you're in a place where you can't use AES. You should then use the -3des
method. Keep in mind all the files outputted will be saved in the directory you're working in. Take note of the full path, as you'll need it later for the NGINX configuration. If you're not sure of the full path of the current directory you're in, you can find out by running the $ pwd
command.
$ openssl genrsa -aes256 -out ca.privkey 4096
You'll then see it generating the key:
Generating RSA private key, 4096 bit long modulus
.................................................................................................................++
.........................++
e is 65537 (0x10001)
And then get asked to enter a pass phrase for the key, and of course re-enter it to make sure you didn't make a mistake the first time.
Enter pass phrase for ca.key:
Verifying - Enter pass phrase for ca.key:
Now we want to use this to create a new X509 CA Root Certificate. In this case, we can generate it for 365 days.
$ openssl req -new -x509 -days 365 -key ca.privkey -out ca.crt
You'll be asked to verify the pass phrase you mentioned above, and again, you'll be asked a series of questions, where the values in the square brackets will be the default values if you don't provide a response:
Enter pass phrase for ca.privkey:
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]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:
Your CA certificate has now been created as ca.crt
.
Now let's create the client key and Certificate Signing Request (CSR). The steps are similar to above, with a couple of slight syntax differences so I won't re-explain it. In this example, I want to create a client cert for Bob's iPad.
$ openssl genrsa -aes256 -out bobs-ipad.privkey 4096
$ openssl req -new -out bobs-ipad.csr -key bobs-ipad.privkey
You'll now have the CSR file and client key files in the directory you're working in.
Let's go ahead and create the certificate for the client
$ openssl x509 -req -days 365 -in bobs-ipad.csr -CA ca.crt -CAkey ca.privkey -set_serial 100 -out bobs-ipad.crt
You'll be asked for the CA's cert private key again as you're doing an operation against it.
If you look at bobs-ipad.crt now, you should see the certificate!
$ cat bobs-ipad.crt
-----BEGIN CERTIFICATE-----
<certificate text goes here>
-----END CERTIFICATE-----
Cool!! Now we need to make something useful of this. Most browsers, utilize the PKCS12 format of certs, so we'll need to export the certs we just created to that format. Once we do this, we can take the output (in the example below, we use the bobs-ipad.p12 file) and install that into our browsers and OS's
$ openssl pkcs12 -export -clcerts -in bobs-ipad.crt -inkey bobs-ipad.privkey -out bobs-ipad.p12
The way you install it into the browser is usually pretty straight-forward, depending on the browser. Google this part :)
Within the server block in the NGINX configuration, you'll need to add the following two lines. Note that /path/to/ca.crt should be the whole path to the CA.crt we noted above.
# Client Certificate for Authentication - OpenSSL
ssl_client_certificate /path/to/ca.crt;
ssl_verify_client on; # this should be set to on so that the server always checks the presented client certs against the CA cert for validity.
My whole config looks something like (I have SSL on). Don't let the SSL configuration scare you, its mostly copied and pasted from best practices. You can find that config at cipherli.st
server {
listen 443 ssl;
server_name server.name;
# Client Certificate for Authentication - OpenSSL
ssl_client_certificate /path/to/ca.crt;
ssl_verify_client on;
# HTTPS Certificate Information - Let's Encrypt
ssl_certificate /path/to/letsencrypt/fullchain.pem;
ssl_certificate_key /path/to/letsencrypt/privkey.pem;
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 208.67.222.222 208.67.220.220 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
# Logging
access_log /var/log/nginx-hass-access.log;
error_log /var/log/nginx-hass-error.log;
location / {
proxy_pass http://hass.url:8123/;
proxy_set_header Host $host;
}
location /api/websocket {
proxy_pass http://hass.url:8123/api/websocket;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
That's it! Restart NGINX for the changes to take into effect, and you should be good!
Run these commands to generate the CA certs, and follow the wizards:
$ openssl genrsa -aes256 -out ca.privkey 4096
$ openssl req -new -x509 -days 365 -key ca.privkey -out ca.crt
Run these commands to create a certificate and export a PKCS12 cert for your browser (in this case, I use bobs-ipad as an example)
$ openssl genrsa -aes256 -out bobs-ipad.privkey 4096
$ openssl req -new -out bobs-ipad.csr -key bobs-ipad.privkey
$ openssl x509 -req -days 365 -in bobs-ipad.csr -CA ca.crt -CAkey ca.privkey -set_serial 100 -out bobs-ipad.crt
$ openssl pkcs12 -export -clcerts -in bobs-ipad.crt -inkey bobs-ipad.privkey -out bobs-ipad.p12
And update your NGINX server block config (replace /path/to/ca.crt with the actual path to your ca.crt file):
ssl_client_certificate /path/to/ca.crt;
ssl_verify_client on;
I am trying to do something similar, with mqtts, with device crt and key.
But I get
2021/04/28 07:34:25 [info] 780885#780885: *5 SSL_do_handshake() failed (SSL: error:14094418:SSL routines:ssl3_read_bytes:tlsv1 alert unknown ca:SSL alert number 48) while SSL handshaking, client: 11.65.81.90, server: 111.166.22.84:8883
as far as I can tell everything is same except the p12 part is not done since its not browser