Key Exchange Protocols (Protocolos de Troca de Chaves)¶
Key exchange protocols allow two parties to establish a shared secret over an insecure channel without having exchanged any secret information beforehand. This is fundamental for secure communications on the internet.
Diffie-Hellman Key Exchange¶
Diffie-Hellman (DH) was one of the first practical key exchange protocols, published in 1976 by Whitfield Diffie and Martin Hellman.
Mathematical Foundation¶
Given: - A large prime $p$ (typically 2048 bits or more) - A generator $g$ of $\mathbb{Z}_p^*$ (typically 2 or 3)
Alice's steps: 1. Choose a private key $a$ randomly from $[2, p-2]$ 2. Compute public value $A = g^a \mod p$ 3. Send $A$ to Bob over the insecure channel
Bob's steps: 1. Choose a private key $b$ randomly from $[2, p-2]$ 2. Compute public value $B = g^b \mod p$ 3. Send $B$ to Alice over the insecure channel
Shared secret computation: - Alice computes: $s = B^a \mod p = (g^b)^a \mod p = g^{ab} \mod p$ - Bob computes: $s = A^b \mod p = (g^a)^b \mod p = g^{ab} \mod p$
Both arrive at the same shared secret $s$, which can be used as a symmetric encryption key.
Security Analysis¶
The security of Diffie-Hellman relies on the discrete logarithm problem: Given $(p, g, y)$ where $y = g^x \mod p$, finding $x$ is computationally infeasible for large values of $p$.
Attack complexity: - Brute force: $O(\sqrt{p})$ using Baby-step Giant-step or Pollard's rho algorithm - For 2048-bit primes, this requires approximately $2^{1024}$ operations - computationally infeasible with current technology
Java Implementation¶
import java.math.BigInteger;
import java.security.SecureRandom;
public class DiffieHellmanKeyExchange {
// Standard parameters from RFC 7919 (modp1)
private static final BigInteger P = new BigInteger("FFFFFFFFFFFFFFFFC90FDAA2" +
"2168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A087" +
"98E3404DDEF9519B3CD3A431B302B026038C13ACFFFFFFFBCFC6EE1BBC7FF59" +
"B88BB9BCB09CBECECDD4EBA3EDBD4547B9280CD73CDA250F164C406CBBA290" +
"5F47E52BDF9D8EFF2A30E1F7BB4CC89B68F15D42A58ED30ABDDA62FFCF4F90" +
"3EBC965FFC9BFD859AC479CA81E99A3ED9B6D1FE609731A", 16);
private static final BigInteger G = new BigInteger("2");
/**
* Generates a Diffie-Hellman key pair.
*/
public static KeyPair generateKeyPair() {
SecureRandom random = new SecureRandom();
// Generate private key (random in range [3, P-2])
byte[] privateKeyBytes = new byte[128];
do {
random.nextBytes(privateKeyBytes);
} while (new BigInteger(1, privateKeyBytes).compareTo(P.subtract(BigInteger.ONE)) >= 0 ||
new BigInteger(1, privateKeyBytes).compareTo(BigInteger.valueOf(2)) <= 0);
BigInteger x = new BigInteger(1, privateKeyBytes);
// Compute public key: Y = g^x mod p
BigInteger y = G.modPow(x, P);
return new KeyPair(y, x);
}
/**
* Computes shared secret from peer's public key.
*/
public static byte[] computeSharedSecret(BigInteger theirPublicKey, PrivateKey myPrivateKey) {
// Shared secret: s = Y^x mod p
BigInteger sharedSecret = theirPublicKey.modPow(myPrivateKey, P);
// Hash the result to get a usable key (key derivation function)
try {
java.security.MessageDigest digest = java.security.MessageDigest.getInstance("SHA-256");
return digest.digest(sharedSecret.toByteArray());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Main demonstration of Diffie-Hellman key exchange.
*/
public static void main(String[] args) throws Exception {
System.out.println("=== Diffie-Hellman Key Exchange Demonstration ===\n");
// Alice generates her key pair
System.out.println("Alice generating key pair...");
KeyPair aliceKeys = generateKeyPair();
BigInteger alicePublic = (BigInteger) aliceKeys.getPublic();
PrivateKey alicePrivate = (PrivateKey) aliceKeys.getPrivate();
System.out.println("Alice's public value: " + alicePublic.toString(16).substring(0, 64) + "...");
// Bob generates his key pair
System.out.println("\nBob generating key pair...");
KeyPair bobKeys = generateKeyPair();
BigInteger bobPublic = (BigInteger) bobKeys.getPublic();
PrivateKey bobPrivate = (PrivateKey) bobKeys.getPrivate();
System.out.println("Bob's public value: " + bobPublic.toString(16).substring(0, 64) + "...");
// Alice computes shared secret using Bob's public key
System.out.println("\nAlice computing shared secret...");
byte[] aliceSecret = computeSharedSecret(bobPublic, alicePrivate);
// Bob computes shared secret using Alice's public key
System.out.println("Bob computing shared secret...");
byte[] bobSecret = computeSharedSecret(alicePublic, bobPrivate);
// Verify they match
boolean secretsMatch = java.util.Arrays.equals(aliceSecret, bobSecret);
System.out.println("\nAlice and Bob have the same shared secret: " + secretsMatch);
if (secretsMatch) {
System.out.println("Shared secret (first 32 chars): " +
new String(aliceSecret, 0, Math.min(32, aliceSecret.length)));
}
}
}
Ephemeral Diffie-Hellman (DHE) and Elliptic Curve DH (ECDH)¶
Modern implementations use ephemeral keys for forward secrecy.
Forward Secrecy¶
Forward secrecy ensures that even if long-term private keys are compromised, past communications remain secure. This is achieved by using temporary (ephemeral) key pairs for each session.
Ephemeral Diffie-Hellman (DHE)¶
In DHE: 1. Each party generates a new ephemeral DH key pair for the session 2. The ephemeral private keys are discarded after key exchange 3. Long-term RSA/ECC keys are only used for authentication, not key exchange
Elliptic Curve Diffie-Hellman (ECDH)¶
ECDH provides equivalent security to traditional DH with much smaller key sizes:
| Security Level | Traditional DH Key Size | ECDH Key Size |
|---|---|---|
| 128-bit | 3072 bits | 256 bits |
| 192-bit | 7680 bits | 384 bits |
Java Implementation with ECDH¶
import java.security.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class ECKeyExchange {
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* Generates an ECDH key pair.
*/
public static KeyPair generateECDHKeyPair() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "BC");
keyGen.initialize(256); // P-256 curve (secp256r1)
return keyGen.generateKeyPair();
}
/**
* Computes shared secret using ECDH.
*/
public static byte[] computeSharedSecret(PublicKey theirPublicKey, PrivateKey myPrivateKey) throws Exception {
// Use KeyAgreement to derive shared secret
KeyAgreement agreement = new KeyAgreement(KeyAgreement.getInstance("ECDH", "BC"));
// Initialize with own private key
agreement.init(myPrivateKey);
// Process peer's public key
agreement.doPhase(theirPublicKey, true);
// Generate the shared secret
SecretKey sharedSecret = agreement.generateSecret();
return sharedSecret.getEncoded();
}
/**
* Demonstrates ECDH key exchange with forward secrecy.
*/
public static void main(String[] args) throws Exception {
System.out.println("=== ECDH Key Exchange Demonstration ===\n");
// Alice generates ephemeral keys for this session
System.out.println("Alice generating ephemeral keys...");
KeyPair aliceKeys = generateECDHKeyPair();
PublicKey alicePublic = aliceKeys.getPublic();
PrivateKey alicePrivate = aliceKeys.getPrivate();
// Bob generates ephemeral keys for this session
System.out.println("Bob generating ephemeral keys...");
KeyPair bobKeys = generateECDHKeyPair();
PublicKey bobPublic = bobKeys.getPublic();
PrivateKey bobPrivate = bobKeys.getPrivate();
// Alice computes shared secret using Bob's public key
byte[] aliceSecret = computeSharedSecret(bobPublic, alicePrivate);
// Bob computes shared secret using Alice's public key
byte[] bobSecret = computeSharedSecret(alicePublic, bobPrivate);
boolean secretsMatch = java.util.Arrays.equals(aliceSecret, bobSecret);
System.out.println("\nAlice and Bob have the same shared secret: " + secretsMatch);
// Demonstrate forward secrecy: even if ephemeral keys are compromised later,
// past sessions remain secure because each session uses different ephemeral keys.
}
}
TLS Key Exchange Modes¶
TLS supports multiple key exchange modes:
| Mode | Description | Forward Secrecy |
|---|---|---|
| RSA | Server encrypts pre-master secret with RSA public key | No |
| DHE | Ephemeral Diffie-Hellman | Yes |
| ECDHE | Ephemeral Elliptic Curve DH | Yes (recommended) |
TLS 1.3 Key Exchange¶
TLS 1.3 only supports forward secrecy modes: - ECDHE: Elliptic curve ephemeral Diffie-Hellman - PSK: Pre-shared keys with ECDHE for key derivation
Security Considerations¶
Common Attacks and Mitigations¶
| Attack | Description | Mitigation |
|---|---|---|
| Man-in-the-middle | Attacker intercepts public values | Use authenticated DH (with certificates) |
| Small subgroup | Attacker forces use of small subgroups | Validate group order, reject invalid parameters |
| Timing attack | Side-channel through timing variations | Use constant-time operations |
Best Practices¶
- Use standard parameters: RFC 7919 for DH, NIST curves for ECDH
- Enable forward secrecy: Prefer DHE/ECDHE over RSA key exchange
- Validate peer certificates: Prevent man-in-the-middle attacks
- Use TLS 1.3: Provides better security and performance
References¶
- RFC 7919: Moduli for Diffie-Hellman Finite Fields
- RFC 5639: Elliptic Curve Cryptography (ECC) Cipher Suites
- RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3