Criptografia Assimétrica: RSA, ECC, Diffie-Hellman e Troca de Chaves: Do Básico ao Avançado Já leu

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

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:

  1. Escolher dois números primos grandes (p e q)
  2. Calcular N = p × q (módulo)
  3. Calcular φ(N) = (p-1) × (q-1)
  4. Escolher um expoente público e tal que mdc(e, φ(N)) = 1
  5. 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.

Referências


Artigos relacionados