Understanding HTTPS Encryption: A Practical Guide with Python
Every time you see the padlock icon in your browser's address bar, HTTPS is quietly doing a lot of cryptographic work behind the scenes. In this post, we'll break down exactly how HTTPS encryption works, and then use Python to actually demonstrate the core ideas — key exchange, symmetric encryption, and certificate verification — so the concepts stop feeling abstract.
1. What is HTTPS, really?
HTTPS (HyperText Transfer Protocol Secure) is just HTTP wrapped inside a security layer called TLS (Transport Layer Security) — the modern successor to SSL. It doesn't change what data is sent; it changes how that data travels between your browser and the server.
HTTPS provides three guarantees:
| Property | What it means |
|---|---|
| Confidentiality | Data is encrypted, so eavesdroppers on the network can't read it |
| Integrity | Data can't be silently modified in transit without detection |
| Authentication | You can verify you're actually talking to the real server, not an impostor |
2. Symmetric vs. Asymmetric Encryption
HTTPS actually uses both types of encryption, at different stages:
- Asymmetric encryption (e.g., RSA, ECDHE) uses a public/private key pair. It's used briefly at the start of the connection to safely agree on a shared secret — but it's computationally expensive.
- Symmetric encryption (e.g., AES) uses a single shared key that both sides use to encrypt and decrypt. It's fast, so it's used for the actual bulk of data transfer once the connection is established.
Demo: Asymmetric key exchange concept in Python
Let's simulate the core idea using the cryptography library. Install it first with pip install cryptography.
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
# --- Server generates an RSA key pair ---
server_private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048
)
server_public_key = server_private_key.public_key()
# --- Client creates a random "session secret" ---
session_secret = b"a-random-32-byte-shared-secret!"
# --- Client encrypts it using the SERVER'S public key ---
encrypted_secret = server_public_key.encrypt(
session_secret,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
print("Encrypted secret sent over the network:")
print(encrypted_secret[:32], "...")
# --- Only the server's PRIVATE key can decrypt it ---
decrypted_secret = server_private_key.decrypt(
encrypted_secret,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
print("Server successfully decrypted:", decrypted_secret)
This mirrors what happens conceptually during a TLS handshake: the client uses the server's public key (found in its certificate) to safely transmit a secret that only the server's private key can unlock. No one intercepting the encrypted blob on the network can read it.
Demo: Symmetric encryption with AES
Once a shared secret is established, HTTPS switches to fast symmetric encryption for the actual data:
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
# The shared secret from the handshake becomes the AES key
key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)
nonce = os.urandom(12) # unique per message
plaintext = b"GET /login HTTP/1.1\r\nHost: example.com\r\n"
# Encrypt (this is what actually travels over the wire)
ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data=None)
print("Encrypted HTTP request:", ciphertext[:32], "...")
# Decrypt on the receiving end
decrypted = aesgcm.decrypt(nonce, ciphertext, associated_data=None)
print("Decrypted:", decrypted)
Notice how much faster and simpler this is compared to RSA — that's exactly why TLS uses asymmetric crypto only to bootstrap a symmetric key, then switches over.
3. The TLS Handshake, Step by Step
1Client Hello — your browser sends supported TLS versions and cipher suites to the server.
2Server Hello + Certificate — the server responds with its chosen cipher suite and its SSL/TLS certificate, containing its public key.
3Certificate Verification — the browser checks the certificate against trusted Certificate Authorities (CAs) to confirm the server is who it claims to be.
4Key Exchange — client and server derive a shared symmetric session key (using ECDHE/RSA as shown above).
5Finished — both sides confirm the handshake and switch to encrypted, symmetric communication using AES (or similar).
4. Inspecting a Real HTTPS Certificate with Python
You don't need to build TLS yourself — Python's standard library and requests already handle it. But you can inspect the certificate details for any live site:
import ssl
import socket
hostname = "www.google.com"
context = ssl.create_default_context()
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
print("TLS version:", ssock.version())
print("Cipher used:", ssock.cipher())
print("Issued to:", cert.get('subject'))
print("Issued by:", cert.get('issuer'))
print("Valid until:", cert.get('notAfter'))
Running this prints exactly which cipher suite and TLS version were negotiated, along with who issued the certificate — the same trust chain your browser checks silently every time you visit a site.
Making a simple HTTPS request
import requests
response = requests.get("https://example.com")
print("Status:", response.status_code)
print("Was the connection encrypted?", response.url.startswith("https"))
verify=False in requests) in production code. Doing so removes protection against man-in-the-middle attacks, defeating the entire purpose of HTTPS.
5. Why This Matters
Without HTTPS, anyone on the same network — a public Wi-Fi hotspot, a compromised router, an ISP — can read or tamper with the data you send, including passwords, cookies, and personal information. HTTPS closes that gap by combining:
- Asymmetric cryptography to safely exchange a secret without ever transmitting it in the clear
- Symmetric cryptography to encrypt the actual traffic efficiently
- Digital certificates and Certificate Authorities to prevent impersonation
Conclusion
HTTPS isn't magic — it's a well-orchestrated handshake between two well-understood cryptographic techniques. By simulating each piece in Python, you can see exactly how a browser and server go from "hello" to a fully encrypted channel in milliseconds. The next time you see that padlock icon, you'll know precisely what it represents.
Tip for Blogger: paste the content between the <body> tags into the HTML view of your post editor. If you want the styling to carry over cleanly, keep the <style> block as well — Blogger will preserve it within the post.

0 Comments