At its most basic, private communication between two persons' devices involve one function, encrypt(msg, key)
, to create an encrypted version of the message, e_msg
, and another function, decrypt(e_msg, key)
, to recover the message.
The mathematicians who create these two functions tell us that the key
is an astronomically large number with certain properties that matter to them but not to us. We're promised that only people or devices that possess the key
can participate in any conversation piped through those functions.
And if the two devices in question are both servers which were created together and deployed securely, this is enough. The key
is also called a secret
and is never transmitted between devices or anywhere else. It sits in each device's config file, permanently hidden from the world.
But for the common case of a web browser wanting to speak with a new web server, this won't work. The browser is on a device completely ignorant of any keys the server uses. So, a new class of encryption algorithms was created.
Asymmetric key algorithms, unlike the above symmetric key algorithms, have two keys. The public key is used in encrypt(msg, publicKey)
while the private key is used in decrypt(e_msg, privateKey)
. The public key is shared freely with anyone and anything, while the private key remains a hidden secret.
This does mean that communication is only one-way with asymmetric key algorithms. If a device generates a pair of keys (i.e. with the command ssh-keygen
) then it can only listen to private messages specifically sent to it, never sending messages back. To send, the other party must generate its own pair of keys, sharing the public key, to set up a second one-way channel moving in the opposite direction.
Example algorithms:
- Symmetric-key algorithms: DES, 3DES, RC4, AES
- Asymmetric-key algorithms: RSA, DSA, EdDSA, ECDSA
Protocols
Asymmetric keys form the basis of web browsers talking to unknown web servers. The browser can send its own public key to the server, perhaps getting the server's public key back in the response, and then private communication can happen. Exchanges of key information as a prelude to what they actually want to talk about is called a protocol.
However, when the mathematicians said these encrypted messages cannot be broken, they put a few astericks on it. A million computers given a million years can brute-force find what astronomically large number was used. If the message being sent is somehow already known it's possible to bring down those millions to something practical. In lieu of the message being completely known, if a part of it is known and we have many examples of messages it's also possible to triangulate the position of the secret number.
And so, a protocol will often only use the devices' permanent keys just long enough to create new, temporary keys, defeating any sampling attempts. It may also attach an expiration date to the temporary keys in which case it will create even newer temporary keys on a periodic basis.
Example Protocols:
- PGP - Pretty Good Protection
- SSL - Secure Sockets Layer
- TLS - Transport Layer Security (successor to SSL)
- HTTPS - literally http plus either SSL or TLS
- SSH - Secure Shell (favorite of ftp, telnet, and github)
- PPP - Point-to-Point Protocol (modem screech)
- IPsec - you might remember IP from such hits as "TCP/IP" and "IPv6: The Enlargening"
- Kerberos - someone was triple-dog dared to use symmetric-key over UDP/IP
- OIDC - OpenID Connect (implemented by companies like Auth0, Keycloak, Okta...)
- SAML - Security Assertion Markup Language (not Sad At Much xmL)
- and many more
Signatures
Next is signatures. Given an official document for, say, purchasing a house, the buyer will be asked to digitally sign the document. Here the buyer's public and private keys are used in reversed roles. The buyer will encrypt(document, privateKey)
and the result is appended to the document.
This means anyone with the buyer's public key (i.e. literally anyone) can decrypt that portion of the document: decrypt(e_document, publicKey)
. This gives no new information of course since they can already read the unencrypted copy, but the very fact that the public key does in fact reproduce the document instead of gibberish means that The Buyer Was Heretm. It also allows seeing if any changes to the unencrypted document have happened after the signing.
Certificates
Lists of public keys and their holders are kept by a certificate authority. At registriation time the CA may require forms of identification from the requestee ranging in importance from government IDs to dot-com domains to just an email address and name, depending upon the type of certificate (and hence, the type of guarantees) the requestee needs. The money and time required for the process vary accordingly.
Once satisfied the CA puts this identifying information with the public key, an expiration date, the requestee's server's domain name (if applicable), and information about the CA itself in a standard X.509 document and digitally signs it. This document is called a public key certificate, or cert.
Now when a browser contacts that server, it can verify the server's cert by using the CA's public key to decrypt the signature and get back a copy of the cert instead of gibberish. After checking the expiration date and the domain name in the cert matches the domain name navigated to, the browser knows it's safe to proceed.
Example Certificate Authorities:
- VeriSign
- VISA
- Microsoft Azure
- Google Cloud
- Cloudflare
- Amazon Web Services
- ... your browser or operating system has a long but incomplete list
Authentication
Certs authenticate servers so clients like web browsers know they're safe, but the reverse isn't yet true. The server needs to authenticate the person on the client device.
In the specific and usual case of the web, once the cert is verified, the HTTPS protocol (specifically the SSL or TLS protocol underneath) takes over, creating a private channel with which to speak to each other. Then javascript or even plain HTML can form submit a login/password combination.
It's worth noting that the password is plaintext in the request's body, but the SSL/TLS connection means it cannot be seen. However, the headers of a HTTP request is outside of the SSL/TLS tunnel, so never place the plaintext password in the URL. It must be in the body, hence the request must also be POST or PUT never GET.
If encrypting HTTP headers is desired, investigate IPsec.
The ol' localhost on HTTPS
When developing a web app, typically http://localhost is the protocol and domain in use. While it's easy to find the magic incantations that allow https://localhost to work, the reason for each of those steps should hopefully be much clearer.
- Create a self-signed certificate.
- Tell your webserver (IIS, nginx, NodeJS something) to use that cert for that domain.
- Put that cert into your browser or operating system's list of trusted certificate authorities, meaning that your laptop now stands beside AWS and VeriSign as the arbiter of good taste... but only for browsers on your laptop.
- If using an IDP like Keycloak that redirects back to your site after login, you might need to inform it that https prefix on your origin is OK for callback.
Closing Remarks
This little article is just the barest brush against this very large and ever-expanding subject. The amount of details left out could easily fill a bookshelf, and it contains very little practical and immediately actionable advice on how to lock something down. But, many practical books on computer security read like a random grab-bag of do's and don't's with very little rhyme or reason as to why things must be this way or that. I hope this brief article will help ground you with a basic foundation so you can better understand those books.
Top comments (0)