When a customer performs a search on our website or when we send a reading from an IOT device to our server, we are sending information across the internet. Securing information as it moves across the internet is very important. There are many way to achieve this but one of the best way is using SSL / TLS certificates. This means the data is encrypted as it moves across the internet and the client can confirm the identity of the server.
What about the other way around? What about the identity of the client? This is usually done by ask the customer to provide a username and password to identify themselves. This works well if the customer is using a mobile device or a laptop, but when it comes to IOT devices this might not be easy. An IoT device could be sitting in the middle of the field, in pouring rain with no screen or keyboard attached to it. So in the case IOT devices, we can use client certificates to confirm the identity of the device.
In this blog, we are going to dig into certificates, private keys, public keys, Certificate Authorities, CSR and many more. We going to get our hands dirty using OpenSSL. It is an open source software that works on many Operating Systems. We will see how easy it is to secure a website in IIS using a self-signed SSL certificate. How we can request a client certificate via the browser. The idea is that the same concepts can be applied to secure any communication like between IOT devices and .netCore APIs … which I’ll probably get to in future posts.
Private Keys
It all starts here. Whether you are planning to create your own Root certificate and act as your own Certificate Authority (CA), to secure your devices in your home. Or you are planning to generate a certificate signing request (CSR) to send to a recognised CA. Whichever route, you have to generate a private key.
The private keys needs to be kept secret and should not be shared with anyone. If you feel a private key has been compromised, then you have to generate a new private key and do the necessary updates to invalidate the previous private key and certificates associated with it.
To generate a new private key without needed a password, we can run the below
openssl genrsa -out myrootprivate.key 2048
To secure your private key with a password, you can do something like the below
openssl genrsa -des3 -out myrootprivate.key 2048
The idea behind password protecting your private key is, even if the key file gets stolen, it cannot be used to sign new certificates without the password. This is good, as it reduces the amount of damage that can be done with the stolen private key file.
When you look in the file you should see something like
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
Public Keys
Usually a private and public key are generated as a pair. The private key file contains both the public and private key. To extract the public key we can run the below:
openssl rsa -in myrootprivate.key -pubout -out myrootpublic.key
When you look in the file you should see something like
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
Whilst it is possible to extract the public key this way, I’ve not found any place using it in it’s raw form. Usually the public key is shared in a certificate.
Certificates
A certificate contains the public key plus some additional information. The additional information can be the Name, also know as Common Name (CN), the organisation name (O), the Location (L), the State (S) the Country (C), the Valid from, the Valid to, the Issuer, the serial number, etc. These certificates have undergone various updates over the years and Version 3 (V3) supports some new extension fields like Subject Alternative Name (SAN). It also means certain certificate fields are no longer used, like RFC2818 has deprecated falling back to the Common Name (CN) field since May 2000 and use of the SAN field has been enforced in many modern browsers.
As far as generating certificates goes, there are 2 options:
- you behave as the Certificate Authority, also know as self signed certificates, this is good for development and home use
- using a recognised Certificate Authority like GoDaddy, DigiCert, Let’s Encrypt, etc
The big difference between a certificates signed by a recognised CA is that it will be trusted by any device immediately. A certificates signed by us will not get recognised till our root certificate is added to the trusted root Certificate Authority list on a device.
Let look at the first option where we are the Certificate Authority, as it will give us an understand of what a recognised CA does when we send them a CSR. First thing we need to do is generate a Root public certificate using our Root private key. This can be done by running:
openssl req -x509 -new -nodes -key myrootprivate.key -sha256 -days 365 -out myrootpublic.crt
You will need to provide some basic information like Organisation name, Country, etc. I’ve entered:
As an alternative you can keep this information in a certificate configuration file and use that to create the certificate.
If you look in the file, you should see something like:
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
Now we have a Root private key and Root public certificates. This combination allows us to mimic a recognised CA. Woopy!!
This Root public certificate need to be added to the Trusted Root Certification Authorities of your computer, if not, the below error will come up when you use any certificate signed by this private and public key pair in IIS.
NET:ERR_CERT_AUTHORITY_INVALID
This server could not prove that it is www.mysecuresite.org; its security certificate is not trusted by your computer's operating system. This may be caused by a misconfiguration or an attacker intercepting your connection.
The easiest way to register this root certificate on windows is to double click myrootpublic.crt file and then click on “Install Certificate” at the bottom. In the Certificate Import Wizard choose “Local Machine” as the Store Location and click “Next”. Then choose the Trusted Root Certificate Authorities as your location to add the certificate. Click OK and “Next” and the certificate should be trusted from now.
How do we look inside a certficate to make sure the fields are correct? We can do this by running:
openssl x509 -noout -text -in clientcerttest.crt
Certificate Signing Request (CSR)
Let say we have a domain, mysecuresite.org and we want to secure any communication from and to it, for example a customer’s login form or a client calling an API. In order to secure the communication we need a certificate containing a public key to share with the browser or any client. In order to get this domain public certificate we need to raising a certificate signing request (CSR) with the domain’s details. And in order to raise a CSR we need a private key for that domain. So lets start by generating a private key for this domain. We already know the command for earlier:
openssl genrsa -out mysecuresite.org.key 2048
The next thing we need is a CSR. Even if we are acting as our own Certificate Authority or using a recognised CA, we need a CSR for our domain. We can do this by running the below. Notice how we are using the domain’s private key to generate a CSR. Our Root public certificate or a recognised CA has not entered the picture yet.
openssl req -new -key mysecuresite.org.key -out mysecuresite.org.csr
You will have to enter the basic information like Organisation Name, Country, etc to create the CSR. I’ve entered:
As an alternative you can keep this information in a certificate configuration file and use that to create the CSR.
When we look in the CSR file we can see something like:
-----BEGIN CERTIFICATE REQUEST-----
...
-----END CERTIFICATE REQUEST-----
How do we look inside a CSR to make sure the fields are correct? We can do this by running:
openssl req -noout -text -in mysecuresite.org.csr
Generate a Certificate from a CSR using our Root private key and Root public certificate
Remember how I mentioned earlier that certificates has been through a few revisions and some new extension fields have been added and some other fields are not longer used. There is where we have to create an extension file with the new required extension fields. If we do not do this and just go ahead and generate and install the certificate in IIS, the browser will complain that the certificate is invalid and give us this error:
NET::ERR_CERT_COMMON_NAME_INVALID
This server could not prove that it is www.mysecuresite.org; its security certificate does not specify Subject Alternative Names. This may be caused by a misconfiguration or an attacker intercepting your connection.
The Subject Alternative Name (SAN) extension field is very important and it indicates which domains this certificate secures. Our extension file, mysecuresite.org.ext should look like:
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.mysecuresite.org
We can run the below to generate the domain certificate. The certificate is signed by our Root private key and Root public certificate
openssl x509 -req -in mysecuresite.org.csr -CA myrootpublic.crt -CAkey myrootprivate.key -CAcreateserial -out mysecuresite.org.crt -days 365 -sha256 -extfile mysecuresite.org.ext
You can open the file and make sure the certificate tags are there. You can also inspect the certificate contents for correct extension fields by using the command mention at the end of the ‘certificate’ section
Generate a Certificate from a CSR using a recognised CA
At this point you just need to hand over the CSR and the extension fields file to a recognised CA or I’m sure they will have a web form where this information can be entered. They will perform the necessary checks and when they are happy they will send the signed certificate back to you. It is very likely that they will follow the same process as we did in the previous section to generate and sign the certificate.
Secure an IIS website
Now that we have a signed domain certificate, we need to import it into IIS at the root level. IIS needs the certificate to be in pfx format in order to import it. A PFX file has both the private key and public certificate in it. This is quiet easy to achieve using openssl, just run the below command to package your domain private key and domain public certificate into a single file:
openssl pkcs12 -export -out mysecuresite.org.pfx -inkey mysecuresite.org.key -in mysecuresite.org.crt
Now, open the IIS Manager, select the top most node and ‘Server Certificates’. Then click on Import…, located the website pfx file and upload it here
Now we can create a site and assign our new certificate to it. If you have an existing site you just need to update the https bindings to associate the certificate with your website. Vola! you should be able to browse to your site and the browser should show a valid certificate.
Final notes
I was using Git Bash to run these openssl command on my Windows 10 machine. One gotcha when using openssl this way is that the password input do not get properly prompted and it looks like openssl is stuck. The best way I have found around this is to use the -passout parameter with stdin. For example in order to create the pfx file I used the below and hit enter for the password as I’ve not used a password for my domain private key
openssl pkcs12 -export -out mysecuresite.org.pfx -inkey mysecuresite.org.key -in mysecuresite.org.crt -passout stdin
You can use the -passout parameter with a file too, to pass in a password.
There are other tool out there that achieve the same results like makecert.exe, but I believe this has been deprecated now. Powershell also has a suite of certificate command in their PKI module worth exploring.
There are a few abbrivate, formats and extensions in connection with SSL / TLS and certificates. I’ve tried to collect them here:
PKI = Public Key Infrastructure
CSR = Certificate Signing Request
PVK = Private Key
CRT / CER = Public Certificate
PFX = Package of PVK and cer file
PEM = Privacy Enhanced Mail is a Base64 encoded DER certificate
DER = Certificates in binary format
SAN = Subject Alternative Name
CRL = Certificate Revocation List
SSL = Secure Socket Layer
TLS = Transport Layer Security
Happy Coding!