SSL: How It Works and Why It Matters
Understanding SSL Certificates: Enhancing Security and Trust Online
How did it start?
I have been curious about what an SSL Certificate is and how it works. This is a newbie's log on the path to understanding a thing or two about how it works.
We casually talk about security and how SSL certificates should be used to make your website more secure. In this blog, I am documenting my learnings, and the end goal for me here is to see if I can enable an SSL certificate (sshh... it's called a TLS Certificate since SSL is long deprecated) on a server locally. Why?
This end goal will help me with a few problems that I face day to day and help me dive deeper to understand the magic.
The Need ...
Why do we need an SSL certificate and what does it certify? The obvious answer found all over the internet is that obtaining an SSL certificate guarantees that any connection made to the website is secure.
Okay! But how?
This is the most interesting part. A superficial answer is that a website or application with an SSL certificate means that any data transfer between the client and server will happen over HTTPS (HTTP over SSL/TLS), so the data will be encrypted, making the transfer more secure.
This explanation raises more questions.
Where is the encryption happening? How is the encryption happening? Is encryption the sole reason we are using HTTPS?
How is this working?
To investigate the working I started banking on what I know with my experience with SSL certificates. I have used Let's Encrypt to obtain and use SSL certificates hence I started working backwards.
An SSL certificate also shows the public that you are who you say you are. To achieve this, we need a Certificate Authority (CA) that has verified the entity and is publicly trusted.
When we install certbot and try to obtain a certificate it:
Generate a private key.
Generate a public key.
Generate a CSR(Certificate Signing Request) and send the CSR to the Certificate Authority(CA).
On receiving the CSR the CA will verify the user, domain etc. and if all goes well it will issue a certificate to the user/domain.
Now, let's try to understand what exactly a certificate is.
A certificate is a file or document that contains the issuer's information, the issuer's public key, and a signature.
The signature is a crucial part of the document because it is used by entities to verify the authenticity of the certificate.
A signature is the checksum of the certificate, calculated using the provided algorithm and then encrypted by the issuer's private key.
Note: For messages, the public key is used to encrypt and the private key is used to decrypt. For signatures, it works the opposite way.
Now that we have obtained a certificate, our CA authenticates the connection by checking the signature.
The question that puzzled me was: how does my machine trust any of these connections, and will there be a lot of back-and-forth to establish a single HTTPS connection?
This brought me to an understanding that each operating system and browser does ship a bunch of public keys for Root Certificate Authority. The Root CA certifies the intermediate CA's like Let's Encrypt and there is a chain of trust that gets verified on each connection.
Hence, the chain of trust is followed and then Root CA Signature gets verified using the public key that is shipped with the Operating System or browser. On Ubuntu, the certificates can be found in /etc/ssl/certs.
Now, that we have an understanding of how security is promised(Not guaranteed) on an HTTPS connection, we can reverse-engineer a few concepts to generate a certificate and force OS/browser to support the certificate.
Generating a Local (Self Signed) Certificate
Various resources will help you generate a self-signed certificate, the ones that I can vouch for are this GitHub gist and the article from Lets Encrypt.
First, we need to generate Root Certificate, Root Primary key and Public Key.
openssl req -x509 -nodes -new -sha256 -days 1024 -newkey rsa:2048 -keyout RootCA.key -out RootCA.pem -subj "/C=US/CN=Farhaan-Root-CA"
After this you should have 3 files in your directory:
Now we need to have an ext
file which has all information about the domain I want the certificate for i.e SAN information:
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = farhaan.local
DNS.3 = farhaan.bukhsh.local
I created a file called farhaan.ext
to achieve the same.
Now I need to file a CSR for the domain and generate the private-public key and then sign it with a Root CA.
openssl req -new -nodes -newkey rsa:2048 -keyout localhost.key -out localhost.csr -subj "/C=IN/ST=YourState/L=YourCity/O=Example-Certificates/CN=farhaan.local"
The above command will give me a private key for the localhost and a Certificate Signing Request file which we can use to generate an SSL Certificate.
and then we run
openssl x509 -req -sha256 -days 1024 -in localhost.csr -CA RootCA.pem -CAkey RootCA.key -CAcreateserial -extfile farhaan.ext -out localhost.crt
This gives us the localhost.crt
file which is the SSL certificate.
Let's Serve
You can see I am serving the local file on a HTTPS connection. There are a few things that needs to be done, I had a simple python 3 server written and borrowed from here.
from http.server import HTTPServer, SimpleHTTPRequestHandler
import ssl
from pathlib import Path
port = 4443
httpd = HTTPServer(("localhost", port), SimpleHTTPRequestHandler)
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain(Path(__file__).parent / "server.pem")
httpd.socket = ssl_context.wrap_socket(
httpd.socket,
server_side=True,
)
print(f"Serving on https://localhost:{port}")
httpd.serve_forever()
after that I need to modify my /etc/hosts
file on Ubuntu to support farhaan.local
.
This is so that farhaan.local redirects to localhost and done. After this we need to make sure Firefox recognizes this so we need to go under `about:preferences` > Privacy & Security > Certificates > View Certificate > import. Here import the RootCA.crt. Now we can visit farhaan.local:4443.
We can see that this is an HTTPS connection.
Here is the certificate:
Conclusion
I got to learn about a bunch of things about SSL, Security, etc. while writing this blog. Let me know if you have any clarification or any improvements. Thanks for reading and happy hacking!