Fundamentos da Criptografia Assimétrica
A criptografia assimétrica, também conhecida como criptografia de chave pública, revolucionou a forma como lidamos com segurança da informação. Diferentemente da criptografia simétrica, que usa a mesma chave para cifrar e decifrar dados, a criptografia assimétrica utiliza um par de chaves matematicamente relacionadas: uma chave pública (que pode ser compartilhada livremente) e uma chave privada (que deve ser mantida em segredo).
O grande diferencial é que dados criptografados com a chave pública só podem ser decriptografados com a chave privada correspondente, e vice-versa. Essa propriedade permite três funcionalidades críticas: confidencialidade (apenas o detentor da chave privada lê as mensagens), autenticação (comprovamos que quem assinou possui a chave privada) e não-repúdio (o signatário não pode negar ter assinado). Todos os sistemas modernos de segurança na internet — desde HTTPS até assinatura digital de documentos — dependem fundamentalmente desses conceitos.
RSA: O Algoritmo que Sustenta a Internet
Teoria por Trás do RSA
O RSA (Rivest-Shamir-Adleman) é o algoritmo assimétrico mais tradicional e amplamente implementado. Sua segurança repousa na dificuldade computacional de fatorar números grandes em seus fatores primos. A geração de chaves envolve:
- Escolher dois números primos grandes (p e q)
- Calcular N = p × q (módulo)
- Calcular φ(N) = (p-1) × (q-1)
- Escolher um expoente público e tal que mdc(e, φ(N)) = 1
- Calcular o expoente privado d tal que (e × d) mod φ(N) = 1
A chave pública é (N, e) e a chave privada é (N, d). Para criptografar uma mensagem m: c = m^e mod N. Para descriptografar: m = c^d mod N.
Implementação Prática com Python
Vou demonstrar uma implementação educacional e depois usar a biblioteca padrão para fins reais:
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.backends import default_backend
# Geração de chaves RSA (2048 bits é padrão atual)
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
# Derivar a chave pública
public_key = private_key.public_key()
# Dados a serem criptografados
mensagem = b"Informacao confidencial que precisa ser protegida"
# Criptografar com chave pública
criptograma = public_key.encrypt(
mensagem,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
print(f"Mensagem original: {mensagem}")
print(f"Criptograma (hex): {criptograma.hex()[:100]}...")
# Descriptografar com chave privada
descriptografia = private_key.decrypt(
criptograma,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
print(f"Descriptografado: {descriptografia}")
# Assinatura digital
assinatura = private_key.sign(
mensagem,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print(f"Assinatura (hex): {assinatura.hex()[:100]}...")
# Verificação de assinatura
try:
public_key.verify(
assinatura,
mensagem,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print("✓ Assinatura válida e verificada!")
except:
print("✗ Assinatura inválida!")
O ponto crítico aqui é o padding. Nunca use RSA puro (sem padding) em produção — é vulnerável a diversos ataques. O padrão OAEP para criptografia e PSS para assinatura são as escolhas seguras atuais.
Criptografia de Curva Elíptica (ECC)
Vantagens e Princípios Matemáticos
A Criptografia de Curva Elíptica oferece segurança equivalente ao RSA com chaves significativamente menores. Enquanto RSA com 2048 bits é considerado seguro, ECC com apenas 256 bits oferece segurança comparável. A matemática envolve operações sobre pontos em uma curva elíptica definida por equações como y² = x³ + ax + b.
A operação fundamental é a multiplicação de ponto escalar: um número inteiro multiplicado por um ponto na curva. Essa operação é fácil de calcular "para frente", mas extremamente difícil de reverter (problema do logaritmo discreto em curvas elípticas). As curvas mais utilizadas são a P-256 (secp256r1), também chamada prime256v1, e a Curve25519, que oferece propriedades de segurança superiores.
Implementação com Python
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.backends import default_backend
# Geração de chaves ECC usando Curve25519 (recomendada)
private_key = ec.generate_private_key(
ec.SECP256R1(), # Alternativa: ec.SECP256K1() para Bitcoin/Ethereum
backend=default_backend()
)
public_key = private_key.public_key()
# Dados a serem assinados
mensagem = b"Documento importante que necessita autenticacao"
# Assinatura com ECDSA
assinatura = private_key.sign(
mensagem,
ec.ECDSA(hashes.SHA256())
)
print(f"Assinatura ECDSA (hex): {assinatura.hex()[:100]}...")
# Verificação de assinatura
try:
public_key.verify(
assinatura,
mensagem,
ec.ECDSA(hashes.SHA256())
)
print("✓ Assinatura ECDSA validada com sucesso!")
except:
print("✗ Assinatura ECDSA inválida!")
# Serializar chaves para armazenamento
private_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
public_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
print(f"\nChave Privada (PEM):\n{private_pem.decode()[:150]}...")
print(f"Chave Pública (PEM):\n{public_pem.decode()[:150]}...")
ECC é especialmente útil em ambientes com restrições de banda ou processamento (IoT, dispositivos móveis) e é o padrão para criptomoedas modernas. Não oferece cifração direto como RSA — é principalmente para assinatura e derivação de chaves.
Protocolo Diffie-Hellman e Troca de Chaves
O Problema Resolvido
Antes de Diffie-Hellman, dois interlocutores que nunca se encontraram não conseguiam estabelecer uma chave simétrica compartilhada de forma segura em um canal não-confiável. O protocolo Diffie-Hellman (DH) resolveu isso permitindo que Alice e Bob concordem com uma chave simétrica sem nunca transmiti-la diretamente.
O protocolo clássico funciona assim: (1) Alice e Bob concordam publicamente em um número primo p grande e um gerador g; (2) Alice escolhe um número secreto a, calcula g^a mod p e envia para Bob; (3) Bob escolhe um número secreto b, calcula g^b mod p e envia para Alice; (4) Alice computa (g^b)^a mod p = g^(ab) mod p; (5) Bob computa (g^a)^b mod p = g^(ab) mod p. Ambos chegam ao mesmo valor g^(ab) mod p, que é a chave compartilhada, sem nunca revelarem a ou b.
ECDH: Diffie-Hellman em Curvas Elípticas
A versão baseada em curvas elípticas (ECDH) oferece segurança superior com chaves menores. É o mecanismo por trás do forward secrecy em TLS 1.3 — cada sessão usa um par de chaves efêmero único.
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
import os
# Alice gera seu par de chaves
alice_private = ec.generate_private_key(
ec.SECP256R1(),
backend=default_backend()
)
alice_public = alice_private.public_key()
# Bob gera seu par de chaves
bob_private = ec.generate_private_key(
ec.SECP256R1(),
backend=default_backend()
)
bob_public = bob_private.public_key()
print("=== Troca Diffie-Hellman em Curva Elíptica ===")
print(f"Chave pública de Alice (hex): {alice_public.public_bytes(encoding=__import__('cryptography').hazmat.primitives.serialization.Encoding.X962, format=__import__('cryptography').hazmat.primitives.serialization.PublicFormat.UncompressedPoint).hex()[:100]}...")
print(f"Chave pública de Bob (hex): {bob_public.public_bytes(encoding=__import__('cryptography').hazmat.primitives.serialization.Encoding.X962, format=__import__('cryptography').hazmat.primitives.serialization.PublicFormat.UncompressedPoint).hex()[:100]}...")
# Alice calcula o segredo compartilhado usando a chave privada dela e a pública de Bob
shared_secret_alice = alice_private.exchange(ec.ECDH(), bob_public)
# Bob calcula o segredo compartilhado usando a chave privada dele e a pública de Alice
shared_secret_bob = bob_private.exchange(ec.ECDH(), alice_public)
# Ambos devem ter o mesmo segredo
print(f"\nSegredo compartilhado (Alice): {shared_secret_alice.hex()[:100]}...")
print(f"Segredo compartilhado (Bob): {shared_secret_bob.hex()[:100]}...")
print(f"Segredos idênticos: {shared_secret_alice == shared_secret_bob}")
# Derivar uma chave de criptografia segura a partir do segredo compartilhado
hkdf = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info=b'chave-para-aes-256',
backend=default_backend()
)
chave_aes_alice = hkdf.derive(shared_secret_alice)
chave_aes_bob = hkdf.derive(shared_secret_bob)
print(f"\nChave AES (Alice): {chave_aes_alice.hex()}")
print(f"Chave AES (Bob): {chave_aes_bob.hex()}")
print(f"Chaves de cifra idênticas: {chave_aes_alice == chave_aes_bob}")
# Agora Alice e Bob podem usar chave_aes para cifrar com AES-256-GCM
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
nonce = os.urandom(12)
cipher = AESGCM(chave_aes_alice)
mensagem = b"Mensagem secreta entre Alice e Bob"
criptograma = cipher.encrypt(nonce, mensagem, None)
print(f"\nMensagem cifrada: {criptograma.hex()[:100]}...")
# Bob descriptografa usando a mesma chave
cipher_bob = AESGCM(chave_aes_bob)
descriptografada = cipher_bob.decrypt(nonce, criptograma, None)
print(f"Descriptografada: {descriptografada}")
Este exemplo mostra a cadeia completa: troca de chaves assimétrica (ECDH) para derivar um segredo compartilhado, seguida de geração de chave simétrica (HKDF) e criptografia simétrica (AES-GCM). Essa combinação oferece segurança, eficiência e forward secrecy — se uma chave privada for comprometida no futuro, sessões passadas não são afetadas.
Ataques, Mitigações e Boas Práticas
Vulnerabilidades Conhecidas
Não existe criptografia perfeita — apenas a adequada para seu contexto. RSA é vulnerável a side-channel attacks se não implementado corretamente. ECC, quando mal implementado, pode ser quebrado por ataques de timing. ECDH é vulnerável ao ataque man-in-the-middle se não houver autenticação das chaves públicas trocadas.
A mitigação principal é: (1) use bibliotecas auditadas (cryptography, libsodium, OpenSSL) ao invés de reinventar; (2) sempre autentique as chaves públicas através de certificados X.509 ou fingerprints confiáveis; (3) mantenha chaves privadas offline quando possível; (4) implemente rotação de chaves regularmente.
Checklist de Implementação Segura
- Tamanho mínimo de chave: RSA ≥ 2048 bits, ECC ≥ 256 bits (P-256 ou Curve25519)
- Padding: Sempre use OAEP para RSA, nunca RSA puro
- Hash: SHA-256 ou superior, nunca MD5 ou SHA-1
- Armazenamento: Chaves privadas criptografadas em disco, proteção de memória
- Certificados: Validar cadeia completa e datas de validade
- Gerador aleatório: Use urandom/getrandom do SO, nunca Math.random()
- TLS: Versão 1.2+ com cipher suites modernas (TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ou equivalente)
Conclusão
Dominar criptografia assimétrica é essencial para qualquer desenvolvedor moderno. Aprendemos que RSA permanece relevante para cifração e assinatura, mas ECC oferece vantagens práticas com chaves menores e igualmente seguras. Compreendemos que Diffie-Hellman e suas variantes (ECDH) resolvem o problema fundamental de estabelecer segredos compartilhados em canais não-confiáveis, formando a base do TLS e forward secrecy. Por fim, a lição mais importante: não implemente criptografia do zero — use bibliotecas auditadas, siga padrões estabelecidos e entenda os conceitos antes de usar a ferramenta.