La lección anterior terminó con un problema sin salida: la app móvil de una fisioterapeuta y la API de Nimbus necesitan una clave simétrica común, pero el único canal que tienen entre ellas es exactamente el que quieren proteger. Durante cuatro mil años ese problema no tuvo solución; en los años setenta se resolvió con una idea que suena imposible la primera vez que se oye: usar dos claves distintas, una que se publica y otra que jamás se comparte. Esta lección desarrolla esa idea —el par de claves, RSA, la criptografía de curva elíptica, el intercambio Diffie-Hellman y la forward secrecy, el cifrado híbrido que explica cómo funciona TLS de verdad, y la firma digital, la única primitiva que da no repudio—. Es importante porque prácticamente todo lo que hace segura la web moderna, desde HTTPS hasta las passkeys y la firma del JWT de Nimbus, es criptografía asimétrica.

Contenido

  1. La idea del par de claves
  2. Qué se hace con la pública y qué con la privada
  3. RSA: el problema difícil de la factorización
  4. Criptografía de curva elíptica: la misma seguridad con claves mucho más cortas
  5. Intercambio de claves Diffie-Hellman
  6. Efímero, ECDHE y forward secrecy
  7. Cifrado híbrido: cómo se usa de verdad
  8. Firma digital: qué garantiza y en qué se diferencia de un MAC
  9. Firmar un recibo de reserva de Nimbus con Ed25519
  10. Inspeccionar un par RSA con openssl
  11. La pregunta que queda abierta y la amenaza cuántica

  1. La idea del par de claves

En un esquema asimétrico se generan dos claves matemáticamente relacionadas:

  • La clave pública: se puede publicar en una web, imprimir en una camiseta o enviar por correo sin cifrar. Su difusión no compromete nada.
  • La clave privada: no sale nunca del sistema que la generó. Idealmente ni siquiera es exportable, como ocurre en las passkeys de FIDO2 de 02-05.

La relación entre ambas es de puerta trampa: es fácil calcular la pública a partir de la privada, y computacionalmente inviable hacer el camino inverso. Toda la criptografía asimétrica se apoya en problemas matemáticos con esa asimetría —multiplicar dos primos grandes es trivial, factorizar el producto no lo es—.

flowchart TB
    G["GENERACION\nUn solo proceso produce\nel par completo"]
    G --> PUB["CLAVE PUBLICA\nSe reparte libremente.\nCifrar / Verificar firmas"]
    G --> PRIV["CLAVE PRIVADA\nNunca sale del sistema.\nDescifrar / Firmar"]
    PRIV -.->|"facil"| PUB
    PUB -.->|"inviable"| PRIV

Esto resuelve la distribución de golpe: para recibir mensajes cifrados basta con publicar la clave pública. Y el número de claves deja de crecer cuadráticamente: n participantes necesitan n pares, no n·(n−1)/2 claves compartidas. Con 10.000 usuarios, 10.000 pares en lugar de cincuenta millones de claves.


  1. Qué se hace con la pública y qué con la privada

Esta tabla es la que más veces se recuerda mal, y equivocarse aquí invierte por completo las garantías:

Operación Se usa la clave... Quién puede hacerlo Qué se consigue
Cifrar Pública del destinatario Cualquiera Solo el destinatario podrá leerlo → confidencialidad
Descifrar Privada del destinatario Solo el destinatario
Firmar Privada del emisor Solo el emisor Autenticidad, integridad y no repudio
Verificar Pública del emisor Cualquiera

La regla mnemotécnica: la clave privada es la que "cierra" tu identidad y "abre" tus mensajes. Firmas con lo que solo tú tienes; descifras con lo que solo tú tienes.

El error habitual es invertirlo, y sus consecuencias son distintas en cada dirección:

  • «Ciframos con la privada para que solo lo lean los nuestros». Falso y peligroso: lo que se cifra con la privada lo descifra cualquiera con la pública, porque la pública es pública. Eso no es cifrar, es —conceptualmente— firmar.
  • «Firmamos con la pública». Imposible: si cualquiera puede firmar, la firma no acredita nada.

Una consecuencia importante para el diseño de Nimbus: en un sistema con miles de clientes, cifrar con clave pública no es lo habitual; lo habitual es acordar una clave simétrica con criptografía asimétrica y cifrar los datos con ella (apartado 7). El cifrado asimétrico directo se reserva para datos muy pequeños, como una clave.


  1. RSA: el problema difícil de la factorización

RSA (Rivest, Shamir y Adleman, 1977) fue el primer esquema asimétrico práctico y sigue siendo omnipresente, especialmente en certificados y firmas de tokens.

La intuición del problema difícil. Multiplicar dos números primos grandes es instantáneo:

p = 61   q = 53   ->   n = p * q = 3233     (trivial)

El camino inverso —dado n, encontrar p y q— es fácil con números pequeños y se vuelve inviable cuando n tiene 2048 o 3072 bits (más de 600 dígitos decimales). La clave pública contiene n; la privada depende de conocer p y q. La seguridad de RSA es exactamente la dificultad de factorizar.

Tamaño de clave RSA Bits de seguridad aprox. Estado en 2026
1024 ~80 Prohibido. Al alcance de adversarios con recursos
2048 ~112 Mínimo aceptable. En retirada progresiva
3072 ~128 Recomendado para claves nuevas de larga vida
4096 ~152 Alta seguridad; notablemente más lento

Tres cosas que hay que saber de RSA para usarlo bien:

  1. Es lento, sobre todo la operación con la clave privada (firmar o descifrar). Recuerda la tabla de rendimiento de 03-02: unas mil operaciones por segundo frente a gigabytes por segundo de AES.
  2. No puede cifrar datos grandes. Con RSA-2048 el límite físico es el tamaño del módulo, y con el relleno obligatorio quedan unos 190 bytes útiles. No es una limitación práctica que se pueda sortear con más claves: es estructural.
  3. El relleno importa tanto como el algoritmo. El esquema antiguo PKCS#1 v1.5 tiene ataques conocidos. Hoy se usa OAEP para cifrar y PSS para firmar. Las librerías serias te obligan a elegirlo explícitamente; nunca copies código que use PKCS1v15 para cifrado nuevo.

  1. Criptografía de curva elíptica: la misma seguridad con claves mucho más cortas

La criptografía de curva elíptica (ECC) se apoya en un problema difícil distinto: el logaritmo discreto sobre los puntos de una curva elíptica. Su gran ventaja práctica es la eficiencia.

Bits de seguridad RSA / DH ECC Diferencia de tamaño
80 1024 160 ~6×
112 2048 224 ~9×
128 3072 256 (P-256, Curve25519) ~12×
192 7680 384 (P-384) ~20×
256 15360 512 (P-521, Curve448) ~30×

Lee bien la fila destacada: una clave ECC de 256 bits ofrece la misma seguridad que una RSA de 3072 bits. Eso significa claves y firmas mucho más pequeñas, handshakes más rápidos y menos consumo, lo que importa especialmente en la app móvil de Nimbus y en cualquier dispositivo con batería.

Curvas que verás en la práctica:

Curva Uso principal Notas
P-256 (secp256r1) TLS, certificados, JWT con ES256 Estándar NIST, soporte universal
P-384 Entornos de alta seguridad Estándar NIST
Curve25519 / X25519 Intercambio de claves Diseño moderno, difícil de implementar mal
Ed25519 Firma digital Rápida, firmas de 64 bytes, sin parámetros que elegir
secp256k1 Cadenas de bloques No la uses fuera de ese contexto

Ed25519 y X25519 son la pareja recomendada por defecto para código nuevo. No porque sean «más matemáticas», sino porque su diseño elimina la mayoría de las decisiones donde un desarrollador podría equivocarse: no hay relleno que elegir, no hay curvas que validar a mano, y el algoritmo de firma es determinista, lo que evita el fallo histórico de las firmas ECDSA con aleatoriedad defectuosa (el mismo tipo de fallo que vimos en 03-01 con el generador de Android).

Una advertencia de nomenclatura, porque genera confusión constante: X25519 sirve para intercambiar claves, Ed25519 sirve para firmar. Son curvas emparentadas pero no intercambiables, y las librerías no te dejarán mezclarlas.


  1. Intercambio de claves Diffie-Hellman

Este es el mecanismo que resuelve el problema con el que cerró 03-02. Diffie-Hellman permite que dos partes que nunca se han visto acuerden un secreto compartido hablando por un canal que el atacante escucha entero, sin que el atacante pueda deducir el secreto.

La analogía de los colores

  1. Ana y la API acuerdan públicamente un color base, digamos amarillo. El atacante lo ve.
  2. Ana elige en secreto un color propio, rojo, y lo mezcla con el amarillo: obtiene naranja, y lo envía. El atacante ve el naranja.
  3. La API elige en secreto azul, lo mezcla con amarillo y obtiene verde, que envía. El atacante ve el verde.
  4. Ana mezcla el verde recibido con su rojo secreto. La API mezcla el naranja recibido con su azul secreto. Ambos obtienen el mismo marrón.
  5. El atacante tiene amarillo, naranja y verde, y no puede obtener el marrón, porque separar una mezcla de pinturas en sus componentes es mucho más difícil que mezclarlas.

Esa dificultad de «separar la mezcla» es, en la matemática real, el problema del logaritmo discreto.

sequenceDiagram
    participant A as App movil (Ana)
    participant R as Red (atacante escucha)
    participant S as API de Nimbus
    A->>A: genera privada a<br/>calcula publica A
    S->>S: genera privada b<br/>calcula publica B
    A->>R: envia A (publica)
    R->>S: A
    S->>R: envia B (publica)
    R->>A: B
    A->>A: secreto = combinar(B, a)
    S->>S: secreto = combinar(A, b)
    Note over A,S: Ambos tienen el MISMO secreto compartido
    Note over R: El atacante vio A y B<br/>y no puede deducir el secreto

Dos precisiones fundamentales:

  • El secreto compartido no se usa directamente como clave. Se pasa por una función de derivación (HKDF, de 03-02) para obtener claves de cifrado con el formato y la separación de dominios correctos.
  • Diffie-Hellman por sí solo no autentica. Un atacante activo puede hacer un intercambio con cada extremo y situarse en medio: es el clásico MITM de 02-02. La solución es autenticar el intercambio con una firma digital respaldada por un certificado —y ahí es donde el módulo enlaza con TLS (03-05) y con la PKI (03-06)—.

  1. Efímero, ECDHE y forward secrecy

En TLS verás las siglas ECDHE: Elliptic Curve Diffie-Hellman Ephemeral. La palabra clave es la última.

  • Estático: las partes usan siempre el mismo par de claves para el intercambio.
  • Efímero: se genera un par nuevo para cada sesión y se destruye al terminar.

De ahí sale la propiedad más valiosa de la criptografía moderna:

Forward secrecy (confidencialidad futura, también llamada perfect forward secrecy): comprometer la clave privada de larga duración del servidor no permite descifrar las sesiones pasadas.

Vale la pena aterrizarlo en Nimbus con un escenario concreto:

  1. Un atacante graba durante meses todo el tráfico cifrado entre las apps móviles y la API. No puede leer nada. Lo guarda igualmente.
  2. Dos años después, consigue la clave privada del servidor de Nimbus —por una intrusión, una copia mal borrada o una orden judicial en otra jurisdicción—.
Sin forward secrecy Con forward secrecy (ECDHE)
La clave de sesión se cifraba con la clave pública del servidor y viajaba dentro del handshake La clave de sesión nunca viajó: se acordó con un par efímero que se destruyó al colgar
Con la clave privada, el atacante descifra todo lo grabado, retroactivamente Con la clave privada, el atacante no puede descifrar nada de lo grabado
Un solo compromiso arruina años de tráfico Un compromiso permite suplantar al servidor de ahí en adelante, pero no leer el pasado

Esta es la razón por la que TLS 1.3 eliminó del estándar todos los modos sin forward secrecy y por la que el intercambio de clave RSA está prohibido en configuraciones modernas (03-05). Y es también la respuesta directa al escenario de «cosechar ahora, descifrar después» del apartado 11.


  1. Cifrado híbrido: cómo se usa de verdad

Ya tenemos las dos piezas y sus limitaciones cruzadas:

  • Lo asimétrico resuelve la distribución pero es lento y no puede con datos grandes.
  • Lo simétrico es rapidísimo pero no puede distribuir su propia clave.

La solución es combinarlos, y se llama cifrado híbrido. Es el patrón que está detrás de TLS, de PGP, del cifrado de sobre de la nube y de casi todo lo demás:

flowchart TB
    A["1. Se genera una CLAVE SIMETRICA\naleatoria, unica para este mensaje\no esta sesion (clave de sesion)"]
    A --> B["2. Los DATOS se cifran con esa clave\nusando AES-GCM: rapido,\nsin limite de tamanio"]
    A --> C["3. La CLAVE SIMETRICA se protege\ncon criptografia asimetrica:\nECDHE (acuerdo) o RSA-OAEP (cifrado)"]
    B --> D["4. Se envia o se almacena:\nclave protegida + datos cifrados"]
    C --> D
    D --> E["5. El destinatario recupera la clave\nsimetrica con su privada y\ndescifra los datos"]

Por qué esto es lo correcto y no un apaño:

  • Se cifran 190 bytes con lo asimétrico en lugar de 3 MB. El coste de la parte lenta es constante y despreciable.
  • El tamaño del mensaje deja de tener límite.
  • Si se usa ECDHE en el paso 3 en lugar de cifrar la clave, se obtiene además forward secrecy.

Este mismo patrón, aplicado al almacenamiento en lugar de a la comunicación, se llama cifrado de sobre (envelope encryption): una clave de datos cifra el objeto y una clave maestra del KMS cifra la clave de datos. Es como cifra Nimbus los adjuntos del bucket A-05, y se desarrolla en 03-06 y 03-07.


  1. Firma digital: qué garantiza y en qué se diferencia de un MAC

Firmar no es cifrar. Una firma digital no oculta nada: el mensaje viaja legible y la firma se adjunta. Lo que aporta es prueba de origen.

El flujo real —y es importante, porque explica por qué firmar un fichero de 4 GB es rápido—:

flowchart LR
    M["MENSAJE\ncualquier tamanio"] --> H["HASH\nSHA-256 -> 32 bytes"]
    H --> F["OPERACION CON LA\nCLAVE PRIVADA\nsobre esos 32 bytes"]
    F --> S["FIRMA\n64 bytes (Ed25519)"]
    M --> V["VERIFICAR:\nrehacer el hash y comprobar\ncon la clave PUBLICA"]
    S --> V

No se firma el mensaje: se firma su hash. Por eso el coste de firmar no depende del tamaño del fichero, y por eso una función hash rota (MD5, SHA-1) rompe también la firma: si el atacante encuentra dos documentos con el mismo hash, una firma válida para uno lo es para el otro. Los hashes se estudian en 03-04.

Firma frente a MAC

MAC (HMAC) Firma digital
Tipo de clave Compartida entre las dos partes Par: privada firma, pública verifica
¿Integridad?
¿Autenticidad? Sí, frente al otro poseedor de la clave Sí, frente a todo el mundo
¿No repudio? No
Coste Muy bajo (es un hash) Alto (operación asimétrica)
¿Quién puede verificar? Solo quien tiene la clave Cualquiera con la pública
Caso en Nimbus Webhook de la pasarela de pago Firma del JWT, DKIM, firma de artefactos

La diferencia decisiva es el no repudio. Si Nimbus y la pasarela comparten una clave HMAC, un webhook con MAC válido demuestra que lo generó alguno de los dos, y como Nimbus también tiene la clave, no sirve como prueba ante un tercero: Nimbus podría haberlo fabricado. Con una firma, en cambio, solo la clave privada de la pasarela puede producirla, y Nimbus no la tiene; por eso la firma sí es prueba.

La regla práctica: usa MAC cuando las dos partes se conocen, comparten un secreto y solo necesitan detectar manipulación; usa firma cuando el verificador puede ser cualquiera o cuando necesitas prueba oponible.


  1. Firmar un recibo de reserva de Nimbus con Ed25519

Nimbus quiere emitir recibos de reserva verificables: la clínica debe poder comprobar, meses después, que ese recibo lo emitió Nimbus y no ha sido alterado.

from cryptography.hazmat.primitives.asymmetric.ed25519 import (
    Ed25519PrivateKey, Ed25519PublicKey,
)
from cryptography.hazmat.primitives import serialization
from cryptography.exceptions import InvalidSignature
import json

# ---------------------------------------------------------------
# 1. GENERACION DEL PAR. Se hace UNA VEZ. La privada va al gestor
#    de secretos o al KMS (03-06); la publica se publica.
# ---------------------------------------------------------------
privada = Ed25519PrivateKey.generate()
publica = privada.public_key()

pub_pem = publica.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
print(pub_pem.decode())

# ---------------------------------------------------------------
# 2. EL MENSAJE. Serializacion CANONICA: mismo objeto -> mismos bytes.
#    sort_keys y separators evitan que un espacio invalide la firma.
# ---------------------------------------------------------------
recibo = {
    "tenant": "CL-014",
    "reserva_id": 88421,
    "paciente_ref": "PX-7731",          # referencia, no el nombre
    "fecha": "2026-08-14T10:30:00+02:00",
    "importe_cent": 4500,
    "emitido_por": "nimbus-reservas",
    "emitido_en": "2026-08-02T09:12:44+02:00",
}
mensaje = json.dumps(recibo, sort_keys=True, separators=(",", ":")).encode("utf-8")

# ---------------------------------------------------------------
# 3. FIRMAR con la clave privada -> 64 bytes
# ---------------------------------------------------------------
firma = privada.sign(mensaje)
print("Longitud de la firma:", len(firma), "bytes")

# ---------------------------------------------------------------
# 4. VERIFICAR con la clave publica
# ---------------------------------------------------------------
def verificar(pem_publica: bytes, mensaje: bytes, firma: bytes) -> bool:
    clave: Ed25519PublicKey = serialization.load_pem_public_key(pem_publica)
    try:
        clave.verify(firma, mensaje)     # no devuelve nada si es valida
        return True
    except InvalidSignature:
        return False

print("Recibo intacto:", verificar(pub_pem, mensaje, firma))

# ---------------------------------------------------------------
# 5. ALTERAR EL MENSAJE: se cambia el importe de 45,00 a 5,00 euros
# ---------------------------------------------------------------
recibo_alterado = dict(recibo, importe_cent=500)
mensaje_alterado = json.dumps(
    recibo_alterado, sort_keys=True, separators=(",", ":")
).encode("utf-8")

print("Recibo alterado:", verificar(pub_pem, mensaje_alterado, firma))

Salida:

-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAJ1n2rQ0kXsF8mB4vC7pLd9TgYhWq3ZeR6aKcNfUx0sM=
-----END PUBLIC KEY-----

Longitud de la firma: 64 bytes
Recibo intacto: True
Recibo alterado: False

Lo que hay que comprender de este código:

  • La clave pública en PEM ocupa tres líneas. Compárala con una clave pública RSA-3072, que ocupa unas quince. Esa es la ventaja de tamaño de ECC del apartado 4.
  • La serialización canónica es imprescindible. sort_keys=True fija el orden de las claves y separators=(",", ":") elimina los espacios. Sin esto, dos serializaciones del mismo objeto producirían bytes distintos y la firma fallaría por un motivo que no es un ataque. Es el fallo de integración número uno con firmas sobre JSON.
  • privada.sign(mensaje) hace internamente el hash. Ed25519 usa SHA-512 por dentro; tú no tocas esa parte, y ese es justamente el objetivo.
  • verify no devuelve True/False: lanza InvalidSignature. Es un diseño deliberado, para que sea imposible ignorar el resultado por descuido con un if mal escrito. Nunca captures esa excepción sin actuar.
  • Cambiar el importe invalida la firma. Un solo campo modificado y la verificación falla: eso es integridad y autenticidad juntas.
  • El recibo lleva paciente_ref, no el nombre. Un documento firmado es difícil de retirar del mundo y suele acabar archivado durante años. Minimiza los datos personales que contiene (02-04; el marco legal, en 06-03).

Nota de validación profesional. Si un recibo firmado va a usarse como prueba con efectos legales o fiscales en España, la firma criptográfica es condición necesaria pero no suficiente: la normativa de firma electrónica y factura electrónica impone requisitos adicionales sobre el certificado, el prestador y la conservación. Valida el diseño con asesoría legal antes de darle valor probatorio; los aspectos normativos se tratan en el módulo 6.


  1. Inspeccionar un par RSA con openssl

Aunque para código nuevo la recomendación sea ECC, RSA sigue estando en la mayoría de certificados y en muchos emisores de JWT, así que hay que saber manejarlo.

# 1. Generar una clave privada RSA de 3072 bits (~128 bits de seguridad)
openssl genpkey -algorithm RSA \
  -pkeyopt rsa_keygen_bits:3072 \
  -out nimbus_privada.pem
chmod 600 nimbus_privada.pem          # SOLO el propietario puede leerla

# 2. Extraer la clave publica del par
openssl pkey -in nimbus_privada.pem -pubout -out nimbus_publica.pem

# 3. Inspeccionar la clave publica
openssl pkey -pubin -in nimbus_publica.pem -text -noout

# 4. Comparar: generar tambien un par Ed25519 y mirar su tamanio
openssl genpkey -algorithm ed25519 -out nimbus_ed25519.pem
ls -l nimbus_privada.pem nimbus_ed25519.pem

Salida del paso 3 (recortada):

Public-Key: (3072 bit)
Modulus:
    00:c4:1f:9a:e7:2b:8d:05:6f:31:a0:4c:d8:77:e2:
    b9:15:3c:80:6a:f4:29:1d:cb:07:5e:a3:44:90:11:
    ...  (384 bytes en total)
Exponent: 65537 (0x10001)

Salida del paso 4:

-rw------- 1 lucia lucia 2484 ago  2 09:41 nimbus_privada.pem
-rw-r--r-- 1 lucia lucia  119 ago  2 09:41 nimbus_ed25519.pem

Lectura de estos resultados:

  • El módulo es el n = p·q del apartado 3, aquí de 3072 bits (384 bytes). Es público. Los factores p y q están solo en la clave privada.
  • El exponente público 65537 es el valor estándar: primo, pequeño y con pocos bits a uno, lo que hace rápida la operación pública. Verlo distinto es una señal de alarma.
  • 2484 bytes frente a 119. La misma tabla del apartado 4, hecha visible en el sistema de ficheros.
  • chmod 600 no es decorativo. Una clave privada legible por todos los usuarios del sistema es un hallazgo de auditoría habitual, y aparecerá otra vez en 03-06 y en 05-06.
  • Nunca uses -des3 ni contraseñas débiles para proteger la clave privada. Si necesita cifrado en reposo, usa cifrado moderno; y si es una clave de servicio, lo correcto es que viva en un gestor de secretos o en un HSM, no en un fichero (03-06).

  1. La pregunta que queda abierta y la amenaza cuántica

¿De quién es realmente esta clave pública?

Toda la lección se ha apoyado en un supuesto que todavía no hemos justificado. Repasa el intercambio del apartado 5: la app móvil recibe una clave pública por la red y la usa para acordar el secreto. ¿Cómo sabe que esa clave es de Nimbus y no de un atacante situado en medio?

Si no hay forma de comprobarlo, el MITM funciona perfectamente: el atacante hace un intercambio con la app haciéndose pasar por Nimbus y otro con Nimbus haciéndose pasar por la app, y descifra y vuelve a cifrar todo lo que pasa. Ambos extremos tendrían cifrado impecable con la persona equivocada.

La criptografía asimétrica traslada el problema, no lo elimina. Ya no hay que distribuir secretos, pero hay que autenticar claves públicas. Ese es el problema que resuelven los certificados digitales y la infraestructura de clave pública, y es el asunto de 03-06. La forma concreta en que TLS lo aplica en cada conexión se ve en 03-05.

Criptografía poscuántica y el «cosechar ahora, descifrar después»

Un ordenador cuántico suficientemente grande podría ejecutar el algoritmo de Shor, que rompe a la vez RSA, Diffie-Hellman y la criptografía de curva elíptica. No los debilita: los resuelve. No existe hoy una máquina así, y las estimaciones públicas hablan de años o décadas, pero eso no hace que el asunto sea irrelevante hoy:

Primitiva Impacto cuántico Respuesta
RSA, DH, ECC Rotos por el algoritmo de Shor Migrar a algoritmos poscuánticos (ML-KEM, ML-DSA, estandarizados por el NIST en 2024)
AES-256 Debilitado a ~128 bits efectivos por Grover Suficiente. Basta con usar 256 bits
SHA-256/512 Debilitados, no rotos Suficiente

Por qué importa ya: «cosechar ahora, descifrar después». Un adversario con recursos puede grabar hoy tráfico cifrado que no puede leer, guardarlo, y descifrarlo cuando disponga de la máquina. Si el dato sigue siendo sensible dentro de quince años, el problema es de hoy.

Para Nimbus, en la práctica: los datos de citas pierden valor con relativa rapidez, así que no es una prioridad de este año. Lo que sí es sensato es (1) usar forward secrecy siempre, que ya limita el daño de escenarios de este tipo; (2) mantener el software de TLS actualizado, porque los navegadores y las librerías están activando modos híbridos poscuánticos (X25519 combinado con ML-KEM) de forma transparente; y (3) llevar un inventario de dónde se usa cada algoritmo, que es lo que hará barata la migración cuando toque. Esa agilidad criptográfica —poder cambiar de algoritmo sin reescribir el sistema— es la conclusión práctica del apartado, y conecta con el byte de versión que introdujimos en el formato de cifrado de 03-02.


Errores Comunes y Consejos

Errores conceptuales

  1. Invertir el uso de las claves. Se cifra con la pública del destinatario y se firma con la propia privada. «Cifrar con la privada» no protege nada.
  2. Creer que la firma cifra. Una firma no oculta el contenido; lo acredita. Si además hace falta confidencialidad, hay que cifrar aparte.
  3. Confundir MAC con firma. Solo la firma da no repudio. Si el verificador puede ser cualquiera, necesitas firma.
  4. Pensar que ECC es «menos segura» por tener claves más cortas. P-256 equivale a RSA-3072. La longitud no es comparable entre familias.
  5. Suponer que una clave pública recibida por la red es de quien dice ser. No lo es hasta que algo lo certifica (03-06).

Errores de implementación

  1. Cifrar datos grandes con RSA. No cabe, y es lentísimo. Usa cifrado híbrido.
  2. Usar PKCS#1 v1.5 en código nuevo. OAEP para cifrar, PSS para firmar; o directamente Ed25519, que no te deja elegir mal.
  3. Firmar JSON sin serialización canónica. Un espacio o un reordenamiento de campos rompe la verificación y produce incidencias que parecen ataques.
  4. Generar claves con random o en dispositivos sin buena entropía. Es la lección de 03-01, y en firmas ECDSA es letal.
  5. Dejar la clave privada en un fichero legible, en el repositorio o en la imagen del contenedor. El problema de 03-06.
  6. Ignorar el resultado de verify. Si no lanza excepción es válida; si la lanza, el mensaje se descarta y se registra el evento. Nunca «se continúa por si acaso».

Consejos

  • Para código nuevo: Ed25519 para firmar, X25519 para intercambiar. Si una interoperabilidad te obliga a RSA, usa 3072 bits con OAEP/PSS.
  • Guarda siempre un identificador de clave (kid) junto a la firma. Sin él, rotar la clave obliga a parar el servicio; con él, la rotación es transparente. Es exactamente lo que hacía el JWKS de 02-05 y se desarrolla en 03-06.
  • Publica tus claves públicas por un canal que el cliente pueda verificar, y ten pensado desde el principio cómo las vas a rotar.
  • Anota en tu inventario técnico qué algoritmo usa cada componente. El día de la migración poscuántica, ese documento será la diferencia entre un proyecto y una crisis.

Ejercicios

Ejercicio 1 — Elegir la primitiva correcta

Para cada requisito de Nimbus indica: qué primitiva usarías (cifrado simétrico, cifrado asimétrico, intercambio de claves, firma o MAC), con qué algoritmo concreto, qué clave usa cada parte y por qué descartas las alternativas.

  1. La app móvil y la API deben acordar una clave de sesión sin haberse visto nunca.
  2. Nimbus debe demostrar ante un juzgado, dos años después, que un recibo lo emitió su sistema.
  3. La API debe comprobar que un webhook viene de la pasarela de pago, con la que comparte un secreto configurado.
  4. Nimbus quiere enviar la clave de descifrado de una copia a la consultora (A-19) para una restauración puntual.
  5. Las actualizaciones del agente que Nimbus instala en los servidores de algunos clientes deben ser verificables por el cliente.
  6. Un adjunto de 18 MB debe guardarse cifrado en el bucket A-05.

Ejercicio 2 — Explicar la forward secrecy

Marta ha leído en una auditoría que «el servidor no ofrece forward secrecy» y pregunta si eso significa que el tráfico va sin cifrar.

Se pide: (a) responde a Marta en dos párrafos sin fórmulas; (b) describe el escenario concreto de daño en Nimbus si la clave privada del servidor se filtrase dentro de tres años, con y sin forward secrecy; (c) explica qué configuración concreta lo garantiza y por qué TLS 1.3 lo resuelve por diseño; (d) relaciona esto con el riesgo de «cosechar ahora, descifrar después».

Ejercicio 3 — Diagnosticar un diseño de firma

Un desarrollador propone este diseño para los recibos:

import hashlib

CLAVE_SECRETA = "nimbus-2026"

def firmar_recibo(recibo: dict) -> str:
    texto = str(recibo)                                        # (1)
    return hashlib.sha256((CLAVE_SECRETA + texto).encode()).hexdigest()   # (2)

def verificar(recibo: dict, firma: str) -> bool:
    return firmar_recibo(recibo) == firma                      # (3)

Se pide: (a) identifica los problemas de las tres líneas marcadas; (b) indica qué propiedad de seguridad cree el autor que está consiguiendo y cuál consigue realmente; (c) explica por qué este diseño no sirve para el requisito de que la clínica verifique el recibo por su cuenta; (d) propón el diseño correcto y justifica la primitiva elegida.


Soluciones

Ejercicio 1

# Primitiva Algoritmo Claves Por qué no las alternativas
1 Intercambio de claves X25519 (ECDHE) dentro de TLS 1.3 Par efímero por sesión en cada extremo Cifrado asimétrico directo perdería forward secrecy; simétrico no puede distribuirse
2 Firma digital Ed25519 (o RSA-PSS si lo exige la interoperabilidad) Privada de Nimbus firma; pública verifica Un MAC no da no repudio: Nimbus tiene la clave y podría haberlo fabricado
3 MAC HMAC-SHA256 Secreto compartido con la pasarela Una firma sería innecesariamente cara y no aporta nada aquí: las dos partes se conocen
4 Cifrado híbrido Clave de la copia cifrada con RSA-OAEP o sobre X25519, hacia la clave pública de la consultora Pública de la consultora cifra; su privada descifra Enviar la clave en claro por correo es el error de 03-01 ej. 1.2. Y el acceso debe ser temporal: rotar después (03-06)
5 Firma digital Ed25519 sobre el artefacto Privada de Nimbus en el CI/CD; pública distribuida con el agente Un MAC obligaría a compartir el secreto con cada cliente: cualquiera podría firmar. Es la lección de SolarWinds (02-06, 03-07)
6 Cifrado simétrico AES-256-GCM con AAD del tenant_id Clave de datos protegida por la clave maestra del KMS (envelope) RSA no puede cifrar 18 MB. La parte asimétrica solo protege la clave

Ejercicio 2

(a) Respuesta a Marta. No: el tráfico va cifrado igualmente y hoy nadie puede leerlo. «Forward secrecy» se refiere a otra cosa —a qué pasaría en el futuro si alguien consiguiera la clave privada del servidor—. Sin forward secrecy, esa clave permitiría descifrar retroactivamente todas las conversaciones antiguas que alguien hubiera grabado; con ella, cada sesión usa un secreto efímero que se destruye al terminar, así que el pasado queda protegido para siempre.

Dicho de otro modo: sin forward secrecy, la clave del servidor es una llave maestra que abre también el archivo histórico; con forward secrecy, abre solo las puertas de hoy en adelante. Por eso la auditoría lo señala aunque no haya ningún problema visible ahora mismo.

(b) Escenario. Un atacante graba el tráfico de las apps móviles durante dieciocho meses. Sin forward secrecy, al obtener la clave privada dentro de tres años descifra los dieciocho meses completos: agendas de clínicas, correos, teléfonos e información que revela indirectamente datos de salud. Con forward secrecy, no descifra ni una sesión; solo podría suplantar al servidor a partir de ese momento, lo que se corta revocando el certificado y rotando la clave (03-06).

(c) Configuración. Permitir únicamente suites con ECDHE y deshabilitar el intercambio de clave RSA estático; en la práctica, exigir TLS 1.3 —que eliminó del estándar todos los modos sin forward secrecy— y mantener TLS 1.2 solo con suites ECDHE mientras haya clientes antiguos. El detalle de configuración está en 03-05.

(d) Relación con «cosechar ahora, descifrar después». Es el mismo patrón —grabar hoy, descifrar mañana— con un habilitador distinto: allí la clave robada, aquí un futuro ordenador cuántico. La forward secrecy no protege frente a la amenaza cuántica por sí sola (el propio intercambio efímero es vulnerable a Shor), pero elimina el escenario más probable y barato, y encaja con la misma respuesta: claves efímeras, algoritmos actualizables e inventario de dónde se usa cada uno.

Ejercicio 3

(a) Problemas:

  1. str(recibo) — serialización no canónica: depende del orden de inserción del diccionario y del repr de Python. El mismo recibo puede producir cadenas distintas y verificaciones fallidas sin ataque alguno. Además no es interoperable con ningún otro lenguaje.
  2. sha256(CLAVE + texto) — es la construcción prefix-MAC, vulnerable al ataque de extensión de longitud en funciones tipo SHA-2: un atacante puede añadir datos al mensaje y calcular una firma válida sin conocer la clave. Para eso existe HMAC, que se estudia en 03-04. Y la clave, además, está en el código y es una cadena adivinable.
  3. == — comparación de secretos no constante en el tiempo: abre un canal lateral de temporización (03-01). Debe usarse hmac.compare_digest.

(b) El autor cree que está consiguiendo una firma con no repudio. Consigue, en el mejor de los casos, un MAC mal construido: integridad y autenticidad frágiles entre partes que comparten el secreto, sin no repudio, y con la clave al alcance de cualquiera con acceso al repositorio.

(c) Por qué no sirve. Para que la clínica verifique por su cuenta, necesitaría CLAVE_SECRETA. Y en cuanto la tiene, puede fabricar recibos indistinguibles de los de Nimbus. Un esquema de clave compartida no puede dar verificación pública: es la diferencia estructural entre MAC y firma.

(d) Diseño correcto. Firma Ed25519 con la clave privada de Nimbus custodiada en el gestor de secretos o KMS, sobre una serialización canónica (json.dumps(..., sort_keys=True, separators=(",", ":"))), incluyendo en el propio recibo un identificador de clave (kid) y la fecha de emisión, y publicando la clave pública en un extremo verificable. Así cualquier clínica verifica sin poder falsificar, la rotación de clave es posible gracias al kid, y el recibo tiene valor probatorio técnico —con la validación legal señalada en el apartado 9—.


Conclusión

Has visto la idea que resolvió el problema con el que cerró la lección anterior. Un par de claves relacionadas por una función de puerta trampa —fácil en un sentido, inviable en el otro— permite publicar una mitad sin perder nada, y con eso desaparece la necesidad de distribuir secretos previos. Tienes fija la tabla que más se confunde: se cifra con la pública y se descifra con la privada; se firma con la privada y se verifica con la pública. Conoces RSA y su problema difícil, sus tamaños vigentes —2048 como mínimo, 3072 como recomendación— y sus dos límites estructurales, la lentitud y la imposibilidad de cifrar datos grandes; y conoces la criptografía de curva elíptica, donde 256 bits equivalen a 3072 de RSA, con Ed25519 para firmar y X25519 para intercambiar como recomendación por defecto para código nuevo.

Has entendido Diffie-Hellman con la analogía de los colores y su versión efímera, ECDHE, de la que sale la propiedad más valiosa del conjunto: la forward secrecy, que hace que robar la clave privada del servidor dentro de tres años no permita leer nada de lo grabado hasta hoy. Has visto el cifrado híbrido —clave simétrica para los datos, criptografía asimétrica solo para proteger esa clave— que es el patrón real de TLS y del cifrado de sobre de los adjuntos de Nimbus. Y has separado con precisión firma y MAC: ambos dan integridad y autenticidad, pero solo la firma da no repudio, porque el verificador no puede fabricarla. Lo has practicado firmando un recibo de reserva con Ed25519 en 64 bytes, comprobando que alterar el importe invalida la firma, y aprendiendo el detalle que rompe la mayoría de las integraciones reales: la serialización canónica.

Pero la lección termina con la misma honestidad con la que terminó la anterior. La criptografía asimétrica no elimina el problema de confianza: lo traslada. Ya no hay que distribuir secretos, pero sigue habiendo que responder a una pregunta sin la cual todo lo anterior se derrumba ante un atacante en medio: esta clave pública que acabo de recibir, ¿es realmente de Nimbus?

En Funciones Hash, HMAC y Almacenamiento de Contraseñas (03-04) damos un paso atrás hacia la primitiva que ya ha aparecido dos veces sin explicación —el hash que se firma en lugar del mensaje— y la estudiamos a fondo: qué propiedades tiene, por qué MD5 y SHA-1 están rotos, cómo se construye correctamente un HMAC para validar el webhook de la pasarela de pago, y, por fin, cómo debe guardar Nimbus las contraseñas de sus usuarios con Argon2id, sal y parámetros calibrados. La respuesta a la pregunta de la clave pública tendrá que esperar a 03-06.

Curso de Fundamentos de Seguridad Informática

Módulo 1: Introducción a la Seguridad Informática

Módulo 2: Ciberseguridad

Módulo 3: Criptografía

Módulo 4: Gestión de Riesgos y Medidas de Protección

Módulo 5: Herramientas y Técnicas de Seguridad

Módulo 6: Buenas Prácticas y Normativas

Módulo 7: Proyecto Final

© Copyright 2026. Todos los derechos reservados