I recently had to write a server that supported connecting via TLS/SSL, which is something that thousands of people do. It took me many hours and many pages of Google search results to learn how TLS basically works from the perspective of someone writing a server, so I wrote this explainer. I hope it helps you get started with TLS better than the explainers I could find.
TLS makes sure nobody is reading or changing the data in your network communications (encryption) and you are communicating with who you think you’re communicating with (authentication).
The encryption part uses public key cryptography. The server and client each have public and private keys that are deployed with them.
The authentication part uses a signature from a third party called a certificate authority (CA). The person deploying the server applies for a certificate from the CA, who provides a signature proving that the CA says the server is owned by the person. The signature is provided to the client when they connect and the client verifies the signature using the CA’s public key which is deployed with the client.
To summarize who needs what:
- TLS-enabled servers need a private key, a public key, and a signature from a certificate authority of their choosing that says they own that public key.
- TLS-enabled clients need a private key, a public key, and the public key of the certificate authority that the server chose.
To say that the client and server using public key cryptography is a simplification, because they actually use public key crytography to establish a shared symettric session key which is faster and more secure since it changes each session. This doesn’t affect how the services are configured or deployed.
To say that the person deploying the server owns the server is a simplification. Really the CA signs an association between the server’s public key and their hostname. The client verifies both that the signature is valid as well as that the hostname in the certificate matches the hostname of the server. If another server were sitting at that hostname, it couldn’t provide a certificate that it owned the hostname because the CA wouldn’t provide a certificate for that hostname to more than one public key.
If a CA gave its signature to any hostname/publickey pair, it wouldn’t be a very good CA. CA’s check that the applicant owns the public key using a signature and check that the applicant owns the hostname by requesting a DNS record be created for that hostname and CA. Only the owner of the hostname could create the record and only the owner of the public key could sign for it. The DNS record says which CA signs the public key, so it doesn’t matter if another CA says it belongs to someone else.
By accepting a CA’s signature in a server’s TLS certificate, the client is trusting the CA to faithfully verify ownership. If the CA also signs ownership of a hostname to another public key (this has happened!), nothing stops the owner of that public key from masquerading as the owner of the hostname, so it’s important for clients to only trust trustworthy CA’s. Let’s Encrypt, for example, is a free open-source non-profit automated CA that’s well trusted in the industry.
You can use yourself as a CA to create a self-signed certificate. A self-signed certificate proves that you own a hostname according to you — practically this proves nothing to anyone other than you. For a public-facing service like a website, any reasonable client won’t trust a self-signed certificate (nobody’s going to trust you to authenticate yourself). If you’re the only one that’s going to be writing a client for your server though, you can totally trust yourself to authenticate yourself and self-signed certificates are cool and good.
While the server needs to present a trustworthy certificate, the client generally does not. The client, if it needs to authenticate at all, usually authenticates with something other than TLS (like passwords and API keys). TLS does support this (it’s called 2-way TLS or MTLS) but it’s not popular. Because the client doesn’t need to authenticate itself, the server doesn’t care how trustworthy the client’s CA is, so the client generally uses a self-signed certificate.
For local development, the hostname isn’t what is signed by the CA, so TLS needs to be effectively disabled. It’s easier in my experience to completely disable TLS than it is to configure TLS to accept self-signed certificates and then generate and use a self-signed certificate.