Criptografia Assimétrica (RSA, ECC)¶
A criptografia assimétrica, também conhecida como criptografia de chave pública, usa um par de chaves matematicamente relacionadas: uma chave pública que pode ser compartilhada abertamente e uma chave privada que deve permanecer secreta. Isso resolve o problema fundamental da comunicação segura sem segredos pré-existentes.
Fundamentos Matemáticos¶
O Problema do Logaritmo Discreto¶
Dado um primo $p$, um gerador $g$ e o valor $y = g^x \mod p$, encontrar $x$ dado $(p, g, y)$ é computacionalmente difícil para valores grandes de $p$. Isso forma a base de: - Troca de Chaves Diffie-Hellman - Criptografia ElGamal
O Problema da Fatoração de Inteiros¶
Dado um número composto grande $n = p \times q$ (onde $p, q$ são primos), encontrar $p$ e $q$ é difícil. Isso forma a base de: - Criptografia RSA
O Problema do Logaritmo Discreto em Curvas Elípticas¶
Encontrar $k$ dado os pontos $P$ e $Q = kP$ em uma curva elíptica é computacionalmente difícil, mesmo para tamanhos de chave relativamente pequenos. Isso permite: - ECC (Criptografia de Curva Elíptica) - chaves menores com segurança equivalente ao RSA
Criptografia RSA¶
O RSA é o algoritmo assimétrico mais amplamente utilizado, baseado na dificuldade de fatorar inteiros grandes.
Fundamento Matemático¶
Dado: 1. Dois números primos grandes $p$ e $q$ (tipicamente 1024 bits cada para uma chave de 2048 bits) 2. Módulo $n = p \times q$ 3. Função totiente de Euler $\phi(n) = (p-1)(q-1)$ 4. Expoente público $e$ (tipicamente 65537, coprimo com $\phi(n)$) 5. Expoente privado $d$ tal que $ed \equiv 1 \mod \phi(n)$
Criptografia: $C = M^e \mod n$
Descriptografia: $M = C^d \mod n$
Onde: - $M$ é a mensagem em texto claro (deve ser menor que $n$) - $C$ é o texto cifrado - Todas as operações são aritmética modular
Processo de Geração de Chaves RSA¶
def gerar_chave_rsa(bits=2048):
# Gerar dois números primos grandes
p = obter_primo_grande(bits // 2)
q = obter_primo_grande(bits // 2)
# Calcular módulo e totiente
n = p * q
phi_n = (p - 1) * (q - 1)
# Escolher expoente público e (tipicamente 65537)
e = 65537
# Verificar gcd(e, phi(n)) == 1
assert math.gcd(e, phi_n) == 1
# Calcular expoente privado d
d = modInverse(e, phi_n)
return {
'n': n,
'e': e,
'd': d,
'p': p,
'q': q
}
Implementação em Java¶
import java.math.BigInteger;
import java.security.*;
import java.util.Base64;
public class CriptografiaRSA {
private static final int TAMANHO_CHAVE = 2048;
/**
* Gera um novo par de chaves RSA.
*/
public static KeyPair gerarParDeChaves() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(TAMANHO_CHAVE);
return keyGen.generateKeyPair();
}
/**
* Cripta dados usando a chave pública do destinatário.
*/
public static String criptografar(String textoPlano, PublicKey chavePublica) throws Exception {
// Para RSA de mensagens grandes, use abordagem híbrida:
// 1. Gerar chave simétrica aleatória
// 2. Criptografar mensagem com chave simétrica (AES)
// 3. Criptografar chave simétrica com chave pública do destinatário (RSA)
// Exemplo simples apenas para mensagens pequenas:
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, chavePublica);
byte[] bytesCifrados = cipher.doFinal(textoPlano.getBytes());
return Base64.getEncoder().encodeToString(bytesCifrados);
}
/**
* Descriptografa dados usando a chave privada.
*/
public static String descriptografar(String textoCodificado, PrivateKey chavePrivada) throws Exception {
byte[] textoCifrado = Base64.getDecoder().decode(textoCodificado);
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, chavePrivada);
byte[] bytesDescriptografados = cipher.doFinal(textoCifrado);
return new String(bytesDescriptografados);
}
public static void main(String[] args) throws Exception {
// Gerar par de chaves
KeyPair parDeChaves = gerarParDeChaves();
PublicKey chavePublica = parDeChaves.getPublic();
PrivateKey chavePrivada = parDeChaves.getPrivate();
String mensagem = "Mensagem secreta para criptografia RSA";
System.out.println("Mensagem original: " + mensagem);
// Criptografar com chave pública
String criptografado = criptografar(mensagem, chavePublica);
System.out.println("Criptografado (Base64): " + criptografado);
// Descriptografar com chave privada
String descriptografado = descriptografar(criptografado, chavePrivada);
System.out.println("Descriptografado: " + descriptografado);
}
}
Tamanhos de Chave RSA e Segurança¶
| Tamanho da Chave | Segurança Estimada | Tempo para Quebrar (estimado) | Recomendação |
|---|---|---|---|
| 1024 bits | ~80 bits | Anos com cluster | Obsoleto |
| 2048 bits | ~112 bits | Séculos | Mínimo |
| 3072 bits | ~128 bits | Milênios | Recomendado |
| 4096 bits | ~152 bits | Extremamente longo | Alta segurança |
Criptografia de Curva Elíptica (ECC)¶
A Criptografia de Curva Elíptica fornece segurança equivalente ao RSA com tamanhos de chave muito menores, tornando-a ideal para ambientes com limitação de largura de banda.
Fundamento Matemático¶
Uma curva elíptica sobre um campo finito é definida pela equação: $$y^2 = x^3 + ax + b \mod p$$
O problema do logaritmo discreto em curvas elípticas (ECDLP): Dados pontos $P$ e $Q = kP$, encontrar $k$ é computacionalmente inviável.
Comparação de Tamanhos de Chave¶
| Nível de Segurança | Tamanho da Chave RSA | Tamanho da Chave ECC |
|---|---|---|
| 128-bit | 3072 bits | 256 bits |
| 192-bit | 7680 bits | 384 bits |
| 256-bit | 15360 bits | 512 bits |
ECDSA (Elliptic Curve Digital Signature Algorithm)¶
O ECDSA é usado para assinaturas digitais e fornece a mesma segurança do RSA com chaves menores.
import java.security.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class AssinaturaECDSA {
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* Gera um par de chaves ECC (curva P-256).
*/
public static KeyPair gerarParDeChaves() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "BC");
keyGen.initialize(256);
return keyGen.generateKeyPair();
}
/**
* Assina dados usando ECDSA.
*/
public static byte[] assinarDados(byte[] mensagem, PrivateKey chavePrivada) throws Exception {
Signature assinatura = Signature.getInstance("SHA256withECDSA", "BC");
assinatura.initSign(chavePrivada);
assinatura.update(mensagem);
return assinatura.sign();
}
/**
* Verifica uma assinatura ECDSA.
*/
public static boolean verificarAssinatura(byte[] mensagem, byte[] assinaturaBytes, PublicKey chavePublica) throws Exception {
Signature assinatura = Signature.getInstance("SHA256withECDSA", "BC");
assinatura.initVerify(chavePublica);
assinatura.update(mensagem);
return assinatura.verify(assinaturaBytes);
}
public static void main(String[] args) throws Exception {
KeyPair parDeChaves = gerarParDeChaves();
PublicKey chavePublica = parDeChaves.getPublic();
PrivateKey chavePrivada = parDeChaves.getPrivate();
byte[] mensagem = "Mensagem para assinar".getBytes();
// Assinar a mensagem
byte[] assinatura = assinarDados(mensagem, chavePrivada);
System.out.println("Comprimento da assinatura: " + assinatura.length + " bytes");
// Verificar a assinatura
boolean isValida = verificarAssinatura(mensagem, assinatura, chavePublica);
System.out.println("Assinatura válida: " + isValida);
}
}
ECDH (Elliptic Curve Diffie-Hellman) - Troca de Chaves¶
O ECDH permite que duas partes estabeleçam um segredo compartilhado usando criptografia de curva elíptica.
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);
return keyGen.generateKeyPair();
}
/**
* Calcula segredo compartilhado usando a chave pública do parceiro.
*/
public static byte[] calcularSegredoCompartilhado(PublicKey chavePublicaDoParceiro, PrivateKey minhaChavePrivada) throws Exception {
// Use ECDH para derivar segredo compartilhado
KeyAgreement agreement = new KeyAgreement(KeyAgreement.getInstance("ECDH", "BC"));
agreement.init(minhaChavePrivada);
agreement.doPhase(chavePublicaDoParceiro, true);
SecretKey segredoCompartilhado = agreement.generateSecret();
return segredoCompartilhado.getEncoded();
}
public static void main(String[] args) throws Exception {
// Alice gera par de chaves
KeyPair chavesAlice = gerarParDeChavesECDH();
PublicKey publicaAlice = chavesAlice.getPublic();
PrivatePrivadaAlice = chavesAlice.getPrivate();
// Bob gera par de chaves
KeyPair chavesBob = gerarParDeChavesECDH();
PublicKey publicaBob = chavesBob.getPublic();
PrivatePrivadaBob = 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);
System.out.println("Alice e Bob têm o mesmo segredo compartilhado: " +
java.util.Arrays.equals(segredoAlice, segredoBob));
}
}
Criptografia ElGamal¶
O ElGamal é baseado no problema do logaritmo discreto e fornece criptografia probabilística.
Fundamento Matemático¶
Dado: - Um primo grande $p$ - Um gerador $g$ de $\mathbb{Z}_p^*$ - Chave pública $(p, g, y)$ onde $y = g^x \mod p$ (chave privada é $x$)
Criptografia: Para criptografar mensagem $m$: 1. Escolha aleatória $k$ 2. Calcule $c_1 = g^k \mod p$ 3. Calcule $c_2 = m \cdot y^k \mod p$ 4. Texto cifrado: $(c_1, c_2)$
Descriptografia: $$m = c_2 \cdot (c_1^x)^{-1} \mod p$$
Implementação em Java¶
import java.math.BigInteger;
import java.security.SecureRandom;
public class CriptografiaElGamal {
// Parâmetros padrão (RFC 3526)
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 ElGamal.
*/
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);
}
}
Considerações de Segurança¶
Melhores Práticas para Criptografia Assimétrica¶
- Use tamanhos de chave adequados: RSA 2048+ ou ECC P-256 mínimo
- Prefira criptografia autenticada: Use padding OAEP para RSA, não PKCS#1 v1.5
- Evite algoritmos crus: Sempre use modos apropriados (RSA-OAEP, ECDSA)
- Use criptografia híbrida: Cripte dados com AES, cripte chave com RSA/ECC
- Implemente validação de certificados: Verifique certificados em conexões TLS
Vulnerabilidades Comuns¶
| Vulnerabilidade | Descrição | Mitigação |
|---|---|---|
| Ataques a subgrupos pequenos | Atacante força uso de subgrupos pequenos | Valide ordem do grupo |
| Ataques por tempo | Canal lateral através de variações de tempo | Use operações de tempo constante |
| Ataques oracle de padding | Explora erros de validação de padding | Use OAEP, não PKCS#1 v1.5 |
Quando Usar Criptografia Assimétrica vs Simétrica¶
| Cenário | Abordagem Recomendada |
|---|---|
| Criptografar arquivos grandes | Híbrida (AES + RSA/ECC para chave) |
| Mensagens seguras | ECDH para troca de chaves, AES para dados |
| Assinaturas digitais | ECDSA ou RSA-PSS |
| Distribuição de chaves | Variantes Diffie-Hellman |
Referências¶
- RFC 8017: PKCS #1: RSA Cryptography Specifications Version 2.2
- RFC 6979: Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)
- RFC 5480: Elliptic Curve Cryptography Subject Public Key Info
- Menezes, A. J., van Oorschot, P. C., & Vanstone, S. A. (1996). Handbook of Applied Cryptography. CRC Press.