Diffie-Hellman lets two parties who have never met agree on a shared secret key, even though everything they send each other is visible to anyone listening. It is the reason your browser can set up an encrypted connection to a website over a network it has no reason to trust.
The problem it solves
Symmetric ciphers such as AES are fast and strong, but both ends need the same key before they can use one. Sending that key in the clear defeats the point, and meeting in person to swap keys doesn't scale to the web. Until Whitfield Diffie and Martin Hellman published their method in 1976, there was no practical public answer.
Their answer is that the key never travels at all. Each side sends a value that is safe to publish, then combines what it received with a secret it kept to itself. Both arrive at the same result independently. Someone who records the whole conversation has the public values but not the secrets, and without a secret they can't do the final step.
How the maths works
In the video, a public bowl and a private seasoning stand in for numbers. The classic version uses arithmetic modulo a prime, where 'mod p' means 'the remainder after dividing by p':
- Both sides agree on a large prime
pand a baseg. These are public, and are usually standard values that everyone uses. - Alice picks a secret number
a. Bob picks a secret numberb. Neither is ever sent. - Alice sends
A = g^a mod p. Bob sendsB = g^b mod p. - Alice computes
B^a mod p. Bob computesA^b mod p.
Step 4 gives both of them the same number, because (g^b)^a and (g^a)^b are both g^(ab). The order in which the secrets are mixed in doesn't matter, which is exactly why the two bowls end up with the same flavour.
A worked example
Take tiny numbers so the arithmetic is visible: p = 23, g = 5, Alice's secret is 4 and Bob's is 3. Here is the whole exchange, with an eavesdropper, Eve, copying everything that crosses the network:
A Diffie-Hellman exchange with an eavesdropper
Step 1 of 7: Alice and Bob agree on the public numbers p = 23 and g = 5, in the open. Eve notes them down.
Python's three-argument pow does modular exponentiation, so the whole exchange fits in a few lines:
p, g = 23, 5 # public
a, b = 4, 3 # private, never sent
A = pow(g, a, p) # Alice sends 4
B = pow(g, b, p) # Bob sends 10
alice_key = pow(B, a, p)
bob_key = pow(A, b, p)
print(alice_key, bob_key) # 18 18Eve has 23, 5, 4 and 10. To get 18 she needs a or b, which means finding the power of 5 that leaves 4 modulo 23. With numbers this small she can simply try them all. With a prime of 2048 bits or more, nobody knows a way to do it in any useful time.
Why an eavesdropper can't reverse it
Going forwards is cheap: computing g^a mod p takes a few thousand multiplications, even for enormous numbers. Going backwards, from A to a, is called the discrete logarithm problem, and for well-chosen parameters the best known methods are hopelessly slow. That one-way gap is the 'you can't take the seasoning back out' step from the video.
Most modern systems use elliptic-curve Diffie-Hellman (ECDH) instead. The idea is the same, public values combined with private ones to reach a shared result, but the known attacks are much weaker against it, so keys can be far smaller for the same security. X25519, which uses 32-byte keys on Curve25519, is the common default today.
It creates a key, it doesn't encrypt
Diffie-Hellman on its own encrypts nothing. Its output is a shared secret, and a real protocol feeds that through a key derivation function (KDF) to produce the actual keys for a symmetric cipher such as AES-GCM or ChaCha20-Poly1305. Here is the same exchange with X25519, using the widely used Python cryptography package:
from cryptography.hazmat.primitives.asymmetric import x25519
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes
alice = x25519.X25519PrivateKey.generate()
bob = x25519.X25519PrivateKey.generate()
# Only the public keys cross the network
shared_a = alice.exchange(bob.public_key())
shared_b = bob.exchange(alice.public_key())
assert shared_a == shared_b
key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info=b"chat session v1",
).derive(shared_a)key is now 32 bytes that both sides hold and nobody watching can compute. Everything after this point is ordinary symmetric encryption.
Forward secrecy
When each connection uses fresh, throwaway secrets (ephemeral Diffie-Hellman, written DHE, or ECDHE for the elliptic-curve version), the session key exists only for that session and is deleted afterwards. An attacker who records the traffic today and steals the server's long-term private key next year still can't decrypt it. That long-term key was only used to prove the server's identity, never to protect the session key. This property is called forward secrecy, or perfect forward secrecy.
Compare the older RSA key exchange in TLS 1.2 and earlier. The browser encrypted the session secret with the server's RSA public key and sent it across. The secret did travel, so anyone with a recording and the server's private key could decrypt every past session. TLS 1.3 removed RSA key exchange, so every full handshake now uses ephemeral Diffie-Hellman.
The protection depends on the 'ephemeral' part. A server that reuses the same Diffie-Hellman key pair for months loses much of the benefit, because stealing that one key opens every session that used it.
Common mistakes
Forgetting authentication
Plain Diffie-Hellman gives you a key shared with someone, but it doesn't tell you who. An attacker in the middle can run one exchange with Alice and another with Bob, relay messages between them, and read everything. Real protocols tie the exchange to an identity: in TLS the server signs the handshake with the key from its certificate, and SSH checks the server's host key. Unauthenticated Diffie-Hellman only stops passive eavesdroppers.
Weak parameters
The security rests on the size and quality of the numbers. The Logjam attack showed that servers still accepting 512-bit 'export-grade' groups could be broken. Use the standard groups your library ships with, or X25519, rather than generating your own.
Using the raw shared secret as a key
The shared value isn't a uniformly random string of bytes. Always run it through a KDF such as HKDF, and include some context in the derivation so keys for different purposes come out different.
Writing it yourself
The toy code above is for understanding only. Real implementations must check the public values they receive and run in constant time, so they don't leak secrets through timing. Use a maintained library, or better, a whole protocol such as TLS.
Where you meet it
- HTTPS: every TLS 1.3 handshake runs ECDHE, very often with X25519.
- SSH: the key exchange at the start of every session is a Diffie-Hellman variant.
- Messaging apps: the Signal protocol combines several Diffie-Hellman exchanges to set up keys and keep replacing them as a conversation goes on.
- VPNs: WireGuard and IPsec both use it to agree session keys.
Key takeaways
- Diffie-Hellman lets two sides agree a shared secret over a public channel without ever sending it.
- Its security rests on a one-way step: easy to compute, infeasible to reverse with proper parameters.
- It only produces a key; a KDF and a symmetric cipher do the actual encryption.
- Fresh ephemeral keys for each session give forward secrecy.
- On its own it has no authentication, so real protocols pair it with signatures and certificates.