Protocolos de Troca de Chaves (Diffie-Hellman)¶
Protocolos de troca de chaves permitem que duas partes estabeleçam um segredo compartilhado em um canal inseguro sem terem trocado nenhuma informação secreta anteriormente. Isso é fundamental para comunicações seguras na internet.
Diffie-Hellman Key Exchange¶
O Diffie-Hellman (DH) foi um dos primeiros protocolos práticos de troca de chaves, publicado em 1976 por Whitfield Diffie e Martin Hellman.
Fundamento Matemático¶
Dado: - Um primo grande $p$ (tipicamente 2048 bits ou mais) - Um gerador $g$ de $\mathbb{Z}_p^*$ (tipicamente 2 ou 3)
Passos da Alice: 1. Escolha uma chave privada $a$ aleatoriamente de $[2, p-2]$ 2. Calcule valor público $A = g^a \mod p$ 3. Envie $A$ para o Bob pelo canal inseguro
Passos do Bob: 1. Escolha uma chave privada $b$ aleatoriamente de $[2, p-2]$ 2. Calcule valor público $B = g^b \mod p$ 3. Envie $B$ para a Alice pelo canal inseguro
Cálculo do segredo compartilhado: - Alice calcula: $s = B^a \mod p = (g^b)^a \mod p = g^{ab} \mod p$ - Bob calcula: $s = A^b \mod p = (g^a)^b \mod p = g^{ab} \mod p$
Ambos chegam ao mesmo segredo compartilhado $s$, que pode ser usado como uma chave de criptografia simétrica.
Análise de Segurança¶
A segurança do Diffie-Hellman depende do problema do logaritmo discreto: Dado $(p, g, y)$ onde $y = g^x \mod p$, encontrar $x$ é computacionalmente inviável para valores grandes de $p$.
Complexidade do ataque: - Força bruta: $O(\sqrt{p})$ usando Baby-step Giant-step ou algoritmo rho de Pollard - Para primos de 2048 bits, isso requer aproximadamente $2^{1024}$ operações - computacionalmente inviável com tecnologia atual
Implementação em Java¶
import java.math.BigInteger;
import java.security.SecureRandom;
public class TrocaDeChaveDiffieHellman {
// Parâmetros padrão do 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");
/**
* Gera um par de chaves Diffie-Hellman.
*/
public static KeyPair gerarParDeChaves() {
SecureRandom random = new SecureRandom();
// Gerar chave privada (aleatória no intervalo [3, P-2])
byte[] bytesChavePrivada = new byte[128];
do {
random.nextBytes(bytesChavePrivada);
} while (new BigInteger(1, bytesChavePrivada).compareTo(P.subtract(BigInteger.ONE)) >= 0 ||
new BigInteger(1, bytesChavePrivada).compareTo(BigInteger.valueOf(2)) <= 0);
BigInteger x = new BigInteger(1, bytesChavePrivada);
// Calcular chave pública: Y = g^x mod p
BigInteger y = G.modPow(x, P);
return new KeyPair(y, x);
}
/**
* Calcula segredo compartilhado a partir da chave pública do parceiro.
*/
public static byte[] calcularSegredoCompartilhado(BigInteger chavePublicaDoParceiro, BigInteger minhaChavePrivada) {
// Segredo compartilhado: s = Y^x mod p
BigInteger segredoCompartilhado = chavePublicaDoParceiro.modPow(minhaChavePrivada, P);
// Hashar o resultado para obter uma chave utilizável (função de derivação de chaves)
try {
java.security.MessageDigest digest = java.security.MessageDigest.getInstance("SHA-256");
return digest.digest(segredoCompartilhado.toByteArray());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Demonstração da troca de chaves Diffie-Hellman.
*/
public static void main(String[] args) throws Exception {
System.out.println("=== Demonstração de Troca de Chaves Diffie-Hellman ===\n");
// Alice gera seu par de chaves
System.out.println("Alice gerando par de chaves...");
KeyPair chavesAlice = gerarParDeChaves();
BigInteger publicaAlice = (BigInteger) chavesAlice.getPublic();
BigInteger privadaAlice = (BigInteger) chavesAlice.getPrivate();
System.out.println("Valor público da Alice: " + publicaAlice.toString(16).substring(0, 64) + "...");
// Bob gera seu par de chaves
System.out.println("\nBob gerando par de chaves...");
KeyPair chavesBob = gerarParDeChaves();
BigInteger publicaBob = (BigInteger) chavesBob.getPublic();
BigInteger privadaBob = (BigInteger) chavesBob.getPrivate();
System.out.println("Valor público do Bob: " + publicaBob.toString(16).substring(0, 64) + "...");
// Alice calcula segredo compartilhado usando chave pública do Bob
System.out.println("\nAlice calculando segredo compartilhado...");
byte[] segredoAlice = calcularSegredoCompartilhado(publicaBob, privadaAlice);
// Bob calcula segredo compartilhado usando chave pública da Alice
System.out.println("Bob calculando segredo compartilhado...");
byte[] segredoBob = calcularSegredoCompartilhado(publicaAlice, privadaBob);
// Verificar se correspondem
boolean segredosCorrespondem = java.util.Arrays.equals(segredoAlice, segredoBob);
System.out.println("\nA Alice e o Bob têm o mesmo segredo compartilhado: " + segredosCorrespondem);
if (segredosCorrespondem) {
System.out.println("Segredo compartilhado (primeiros 32 chars): " +
new String(segredoAlice, 0, Math.min(32, segredoAlice.length)));
}
}
}
Diffie-Hellman Efêmero (DHE) e ECDH de Curva Elíptica¶
Implementações modernas usam chaves efêmeras para sigilo futuro.
Sigilo Futuro (Forward Secrecy)¶
O sigilo futuro garante que mesmo se chaves privadas de longo prazo forem comprometidas, comunicações passadas permanecem seguras. Isso é alcançado usando pares de chaves temporárias (efêmeras) para cada sessão.
Diffie-Hellman Efêmero (DHE)¶
No DHE: 1. Cada parte gera um novo par de chaves DH efêmero para a sessão 2. As chaves privadas efêmeras são descartadas após a troca de chaves 3. Chaves RSA/ECC de longo prazo são usadas apenas para autenticação, não para troca de chaves
Diffie-Hellman de Curva Elíptica (ECDH)¶
O ECDH fornece segurança equivalente ao DH tradicional com tamanhos de chave muito menores:
| Nível de Segurança | Tamanho da Chave DH Tradicional | Tamanho da Chave ECDH |
|---|---|---|
| 128-bit | 3072 bits | 256 bits |
| 192-bit | 7680 bits | 384 bits |
Implementação Java com ECDH¶
import java.security.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class TrocaDeChaveEC {
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* Gera um par de chaves ECDH.
*/
public static KeyPair gerarParDeChavesECDH() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "BC");
keyGen.initialize(256); // Curva P-256 (secp256r1)
return keyGen.generateKeyPair();
}
/**
* Calcula segredo compartilhado usando ECDH.
*/
public static byte[] calcularSegredoCompartilhado(PublicKey chavePublicaDoParceiro, PrivateKey minhaChavePrivada) throws Exception {
// Use KeyAgreement para derivar segredo compartilhado
KeyAgreement agreement = new KeyAgreement(KeyAgreement.getInstance("ECDH", "BC"));
// Inicializar com própria chave privada
agreement.init(minhaChavePrivada);
// Processar chave pública do parceiro
agreement.doPhase(chavePublicaDoParceiro, true);
// Gerar segredo compartilhado
SecretKey segredoCompartilhado = agreement.generateSecret();
return segredoCompartilhado.getEncoded();
}
/**
* Demonstra troca de chaves ECDH com sigilo futuro.
*/
public static void main(String[] args) throws Exception {
System.out.println("=== Demonstração de Troca de Chaves ECDH ===\n");
// Alice gera chaves efêmeras para esta sessão
System.out.println("Alice gerando chaves efêmeras...");
KeyPair chavesAlice = gerarParDeChavesECDH();
PublicKey publicaAlice = chavesAlice.getPublic();
PrivateKey privadaAlice = chavesAlice.getPrivate();
// Bob gera chaves efêmeras para esta sessão
System.out.println("Bob gerando chaves efêmeras...");
KeyPair chavesBob = gerarParDeChavesECDH();
PublicKey publicaBob = chavesBob.getPublic();
PrivateKey privadaBob = chavesBob.getPrivate();
// Alice calcula segredo compartilhado usando chave pública do Bob
byte[] segredoAlice = calcularSegredoCompartilhado(publicaBob, privadaAlice);
// Bob calcula segredo compartilhado usando chave pública da Alice
byte[] segredoBob = calcularSegredoCompartilhado(publicaAlice, privadaBob);
boolean segredosCorrespondem = java.util.Arrays.equals(segredoAlice, segredoBob);
System.out.println("\nA Alice e o Bob têm o mesmo segredo compartilhado: " + segredosCorrespondem);
// Demonstrar sigilo futuro: mesmo se chaves efêmeras forem comprometidas posteriormente,
// sessões passadas permanecem seguras porque cada sessão usa diferentes chaves efêmeras.
}
}
Modos de Troca de Chave TLS¶
O TLS suporta múltiplos modos de troca de chaves:
| Modo | Descrição | Sigilo Futuro |
|---|---|---|
| RSA | Servidor cripta segredo pré-mestre com chave pública RSA | Não |
| DHE | Diffie-Hellman efêmero | Sim |
| ECDHE | Diffie-Hellman efêmero de curva elíptica | Sim (recomendado) |
Troca de Chave TLS 1.3¶
O TLS 1.3 só suporta modos com sigilo futuro: - ECDHE: Diffie-Hellman efêmero de curva elíptica - PSK: Chaves pré-compartilhadas com ECDHE para derivação de chaves
Considerações de Segurança¶
Ataques Comuns e Mitigações¶
| Ataque | Descrição | Mitigação |
|---|---|---|
| Homem-no-meio (Man-in-the-middle) | Atacante intercepta valores públicos | Use DH autenticado (com certificados) |
| Subgrupo pequeno | Atacante força uso de subgrupos pequenos | Valide ordem do grupo, rejeite parâmetros inválidos |
| Ataque por tempo | Canal lateral através de variações de tempo | Use operações de tempo constante |
Melhores Práticas¶
- Use parâmetros padrão: RFC 7919 para DH, curvas NIST para ECDH
- Ative sigilo futuro: Prefira DHE/ECDHE sobre troca RSA
- Valide certificados do parceiro: Prevenir ataques homem-no-meio
- Use TLS 1.3: Fornece melhor segurança e desempenho
Referências¶
- 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