Public key cryptography is the only way we can have secure digital conversations without first agreeing on a secret.
Let’s see it in action. Imagine Alice wants to send a secret message to Bob. She uses Bob’s public key to encrypt it. This public key is just that: public. Anyone can have it, including Mallory, who’s eavesdropping. Mallory intercepts the encrypted message. She tries to decrypt it with Bob’s public key, but that doesn’t work. She needs Bob’s private key, which only Bob has. Bob receives the encrypted message, uses his private key to decrypt it, and reads Alice’s secret.
from cryptography.fernet import Fernet
# Alice generates a key pair
# In a real scenario, this would be done once per user
alice_key = Fernet.generate_key()
alice_fernet = Fernet(alice_key)
# Bob generates a key pair
bob_key = Fernet.generate_key()
bob_fernet = Fernet(bob_key)
# Alice wants to send a secret to Bob
message = b"Meet me at the usual place at midnight."
# Alice encrypts the message using Bob's public key (which is just his Fernet key in this simplified example)
# In real public key crypto, Alice would get Bob's *public* key, not his Fernet key directly.
# This example uses Fernet for simplicity, which is symmetric, but demonstrates the *concept* of asymmetric encryption.
# For true asymmetric, we'd use RSA or ECC.
# Let's simulate Bob's public key being available to Alice.
# We'll use Bob's generated key as his "public" key for this demo.
bob_public_key_for_alice = bob_key # This is what Alice would obtain.
# Alice encrypts using Bob's "public" key
encrypted_message = Fernet(bob_public_key_for_alice).encrypt(message)
print(f"Original message: {message}")
print(f"Encrypted message (using Bob's key): {encrypted_message}")
# Mallory intercepts the message
mallory_intercepted_message = encrypted_message
# Mallory tries to decrypt with Bob's *public* key (which is actually Bob's private key in this sim)
# This is where the concept breaks down slightly with Fernet, as Fernet keys are symmetric.
# In true asymmetric crypto, Mallory *could not* decrypt with the public key.
# Let's pretend Mallory has a *different* key she thinks is Bob's public key.
# For this demo, we'll just show that the *wrong* key doesn't work.
wrong_key_for_mallory = Fernet.generate_key()
try:
decrypted_by_mallory = Fernet(wrong_key_for_mallory).decrypt(mallory_intercepted_message)
print(f"Mallory decrypted (incorrectly): {decrypted_by_mallory}")
except Exception as e:
print(f"Mallory failed to decrypt: {e}")
# Bob receives the message
# Bob uses his *private* key to decrypt
bob_private_key = bob_key # Bob's private key is the same as his generated key in Fernet.
decrypted_message = Fernet(bob_private_key).decrypt(mallory_intercepted_message)
print(f"Bob decrypted message: {decrypted_message}")
This ability to encrypt with one key and decrypt with another is the core of public key cryptography, also known as asymmetric cryptography. It solves the fundamental problem of key distribution. In symmetric cryptography (where the same key is used for encryption and decryption), you have to securely share that secret key with your communication partner before you can send anything secret. This is a huge hurdle, especially over untrusted networks like the internet. Public key cryptography bypasses this by allowing you to publish your "public key" widely, knowing that only someone with your corresponding "private key" can actually unlock the messages.
The magic behind this lies in mathematical problems that are easy to compute in one direction but astronomically difficult to reverse. The most common implementations rely on either:
- Integer factorization: The difficulty of finding the prime factors of a very large number. RSA is the classic example. If you have a large number
N = p * q, it’s easy to multiplypandqto getN. But if you’re only givenN, findingpandqis incredibly hard. Your public key might be related toN, while your private key is related topandq. - Discrete logarithms: The difficulty of finding the exponent
xin an equation likeg^x mod p = h, giveng,h,p, and the fact thatxis an integer. Elliptic Curve Cryptography (ECC) is a modern, more efficient standard that uses variations of this problem.
Your public key is essentially a large number (or set of numbers) derived from these hard mathematical problems. Your private key is the secret information (like the prime factors or the exponent) that allows you to solve the reverse problem. When Alice encrypts with Bob’s public key, she’s performing a computation based on that public number. Bob, with his private key, can perform a different, inverse computation that undoes Alice’s work and reveals the original message.
Beyond just encryption, public key cryptography is crucial for digital signatures. Here’s how that works: Alice wants to send a message and prove it’s really from her, and that it hasn’t been tampered with. She takes a hash (a unique fingerprint) of her message and then encrypts that hash with her own private key. This encrypted hash is her signature. She sends the original message along with her signature. Bob receives them. He uses Alice’s public key to decrypt the signature, revealing the original hash Alice created. He then calculates his own hash of the received message. If his calculated hash matches the one he decrypted from Alice’s signature, he knows two things: the message came from Alice (because only she has her private key to create that signature) and the message hasn’t been altered (because the hashes match).
A key concept that’s often misunderstood is the role of key exchange protocols, like Diffie-Hellman. Public key crypto doesn’t directly encrypt all your internet traffic. Instead, it’s used in a handshake process. When you connect to a secure website (HTTPS), your browser and the server use public key cryptography (often Diffie-Hellman or RSA key exchange) to agree on a shared secret key. This shared secret key is then used for symmetric encryption (like AES) for the rest of the communication. This is because symmetric encryption is much faster than asymmetric encryption, and public key crypto is primarily used to solve the initial secure key establishment problem. The browser and server perform a complex dance where they exchange public information, and through a series of calculations involving their private keys, they both arrive at the exact same symmetric key without ever sending that key over the wire.
The next step after understanding public key cryptography is often delving into the specifics of Transport Layer Security (TLS), the protocol that underpins HTTPS and secures most web traffic, and understanding how it orchestrates key exchange, authentication, and symmetric encryption.