Al final del módulo anterior quedó al descubierto la grieta que recorre todo lo construido hasta ahora: AES-GCM, HMAC, hasta el pepper de Argon2 — todo presupone que las dos partes ya comparten una clave secreta. Llevamos desde 02-01 aparcando la pregunta de cómo llegó esa clave a ambos lados. Esta lección da el salto conceptual más grande del curso: la criptografía asimétrica, donde las claves ya no son un secreto compartido sino un par — una mitad se guarda bajo llave y la otra se publica a los cuatro vientos. Verás la intuición matemática que lo hace posible (funciones de un solo sentido con trampilla), construirás un RSA de juguete con enteros pequeños para entender qué pasa por dentro, y luego lo harás bien con pyca/cryptography: claves de 3072 bits, padding OAEP y serialización PEM. Al terminar, la Clínica Sol podrá enviar un mensaje cifrado a MediNube sin haber acordado ninguna clave antes — y descubrirás que eso abre dos puertas nuevas y deja una pregunta incómoda sin responder.
Contenido
- El cambio de paradigma: un par de claves
- Funciones de un solo sentido con trampilla
- RSA de juguete: las matemáticas en pequeño
- RSA real con pyca/cryptography
- Por qué el RSA "de libro de texto" es inseguro: padding OAEP
- Los límites fundamentales de RSA: tamaño y velocidad
- Serialización de claves: el formato PEM
- La pregunta incómoda: ¿de quién es esta clave pública?
El cambio de paradigma: un par de claves
Toda la criptografía de los módulos 2 y 3 es simétrica: la misma clave cifra y descifra, la misma clave genera y verifica el MAC. Eso obliga a que emisor y receptor compartan un secreto, y compartir secretos por una red donde cualquiera escucha es justo el problema que no sabemos resolver.
En 1976, Diffie y Hellman propusieron romper la simetría: que cada participante tenga dos claves matemáticamente ligadas:
- La clave privada: se genera localmente, jamás sale de la máquina de su dueño. Es el único secreto.
- La clave pública: se deriva de la privada y se puede publicar en cualquier sitio — una web, un correo, un repositorio. Que un atacante la tenga no es un problema: está diseñada para ser pública.
La magia está en la relación entre ambas: lo que una hace, solo la otra lo deshace. De ahí salen las dos operaciones fundamentales de la criptografía asimétrica:
| Operación | Quién usa qué | Qué consigue | Objetivo (módulo 1) |
|---|---|---|---|
| Cifrar hacia el dueño | Cualquiera cifra con la pública; solo el dueño descifra con la privada | Que solo el destinatario pueda leer | Confidencialidad |
| Firmar por el dueño | El dueño firma con la privada; cualquiera verifica con la pública | Que cualquiera pueda comprobar quién lo emitió | Autenticidad + no repudio |
Fíjate en la asimetría de cada fila: en el cifrado, muchos pueden escribir, solo uno puede leer; en la firma, solo uno puede escribir, muchos pueden comprobar. Este es el mapa completo de lo que la clave pública ofrece — en esta lección desarrollamos solo la primera fila (cifrar). La segunda, la firma digital que nos debe el no repudio prometido desde el módulo 1, tiene su propia lección: 04-03.
Para MediNube esto cambia el juego: la Clínica Sol puede publicar su clave pública, MediNube la suya, y a partir de ahí cualquiera puede enviarles datos cifrados sin reunión previa, sin canal seguro, sin secreto compartido.
flowchart LR
subgraph ClinicaSol["Clínica Sol (emisor)"]
M[Mensaje en claro] -->|cifra con la PÚBLICA de MediNube| C[Cifrado]
end
C -->|red insegura: cualquiera lo ve, nadie lo lee| D
subgraph MediNube["MediNube (destinatario)"]
D[Cifrado] -->|descifra con su PRIVADA| M2[Mensaje en claro]
end
Funciones de un solo sentido con trampilla
¿Cómo puede ser que conocer la clave pública no permita deducir la privada? La respuesta son las funciones de un solo sentido con trampilla (trapdoor one-way functions): operaciones fáciles de calcular en una dirección y computacionalmente inviables de invertir... salvo que conozcas un dato extra (la trampilla).
La trampilla de RSA es la factorización de enteros:
- Fácil (multiplicar): dados dos primos grandes
pyq, calcularn = p × qes instantáneo, incluso con números de mil cifras. - Inviable (factorizar): dado solo
n, recuperarpyqes un problema para el que no se conoce ningún algoritmo eficiente en ordenadores clásicos. Connde 3072 bits, todos los ordenadores del planeta trabajando juntos no acabarían antes de que se apague el Sol.
Compruébalo tú mismo con Python puro:
import time
p = 1000000007 # primo "pequeño" (10 cifras)
q = 998244353 # otro primo
t0 = time.perf_counter()
n = p * q # dirección fácil
print(f"n = {n} calculado en {time.perf_counter() - t0:.9f} s")
# La dirección difícil: dado solo n, encontrar p y q a fuerza bruta.
t0 = time.perf_counter()
divisor = 3
while n % divisor != 0:
divisor += 2
print(f"Factor {divisor} encontrado en {time.perf_counter() - t0:.3f} s")Multiplicar tarda nanosegundos; factorizar este n diminuto de ~60 bits ya cuesta segundos o minutos. Y el coste de factorizar crece exponencialmente con el tamaño, mientras que multiplicar sigue siendo barato. Sobre esa desproporción se sostiene RSA: quien conoce p y q (la trampilla) puede construir la clave privada; quien solo ve n (parte de la clave pública), no.
RSA de juguete: las matemáticas en pequeño
Aviso de juguete (regla de oro 1). Lo que sigue usa primos ridículamente pequeños y omite todas las protecciones. Sirve solo para entender el mecanismo. Jamás implementes RSA a mano en producción: usa
pyca/cryptography, como haremos en el siguiente apartado.
La generación de claves RSA sigue cuatro pasos:
# --- RSA DE JUGUETE: solo para aprender ---
# Paso 1: elegir dos primos secretos p y q.
p, q = 61, 53
# Paso 2: calcular el módulo n (público) y phi (secreto).
n = p * q # 3233 → parte de la clave pública
phi = (p - 1) * (q - 1) # 3120 → solo calculable si conoces p y q
# Paso 3: elegir el exponente público e (coprimo con phi).
e = 17 # en el mundo real casi siempre 65537
# Paso 4: calcular el exponente PRIVADO d, inverso de e módulo phi.
d = pow(e, -1, phi) # 2753 → LA TRAMPILLA en acción
print(f"Clave pública: (n={n}, e={e})")
print(f"Clave privada: (n={n}, d={d})")Punto clave: para calcular d hace falta phi, y para calcular phi hacen falta p y q. Quien solo conoce n = 3233 tendría que factorizarlo primero. Con n = 3233 lo haces de cabeza; con n de 3072 bits, nadie puede.
Cifrar y descifrar son la misma operación con exponentes distintos — elevar módulo n:
m = 65 # el "mensaje" (un número menor que n)
# Cifrar con la clave PÚBLICA (cualquiera puede):
c = pow(m, e, n) # 65^17 mod 3233 = 2790
print(f"Cifrado: {c}")
# Descifrar con la clave PRIVADA (solo el dueño puede):
recuperado = pow(c, d, n) # 2790^2753 mod 3233 = 65
print(f"Descifrado: {recuperado}")
assert recuperado == mFunciona por el teorema de Euler: (m^e)^d = m^(e·d) ≡ m (mod n) cuando e·d ≡ 1 (mod phi). No necesitas dominar la demostración; necesitas la intuición: elevar a e revuelve el mensaje; solo el exponente d — que exige la trampilla — lo desrevuelve.
Observa también la limitación estructural: el "mensaje" es un número menor que n. RSA no cifra flujos de datos; cifra un número de tamaño acotado. Guarda ese dato: será central en el apartado 6.
RSA real con pyca/cryptography
En el mundo real la generación de claves tiene docenas de sutilezas (primos seguros, tests de primalidad, primos con distancia adecuada...). Todo eso lo resuelve pyca/cryptography:
from cryptography.hazmat.primitives.asymmetric import rsa
clave_privada = rsa.generate_private_key(
public_exponent=65537, # el estándar de facto; no lo cambies
key_size=3072, # bits del módulo n
)
clave_publica = clave_privada.public_key()
print(clave_privada.key_size) # 3072
print(clave_publica.public_numbers().e) # 65537
print(clave_publica.public_numbers().n.bit_length()) # 3072public_exponent=65537: elede nuestro juguete. 65537 (2¹⁶+1) es el valor universal: hace el cifrado rápido sin debilitar nada. No hay motivo para usar otro.key_size: el tamaño denen bits, que define la seguridad. 2048 bits es el mínimo aceptable hoy (~112 bits de seguridad); 3072 o más es lo recomendado para sistemas nuevos (~128 bits de seguridad, nuestro estándar del curso según la regla de oro 5). Por debajo de 2048, roto o al borde: prohibido.- El objeto
clave_privadacontiene la pública (se deriva de ella conpublic_key()); lo contrario es imposible — esa es toda la gracia.
Por qué el RSA "de libro de texto" es inseguro: padding OAEP
Nuestro juguete hacía c = m^e mod n directamente sobre el mensaje. Eso se llama RSA de libro de texto (textbook RSA) y es inseguro por varias vías independientes:
- Es determinista. El mismo mensaje produce siempre el mismo cifrado. Un atacante que sospeche que el mensaje es
"POSITIVO"o"NEGATIVO"no necesita descifrar nada: cifra ambos candidatos con la clave pública (¡que es pública!) y compara. Es el mismo pecado que el modo ECB en 02-02 — el determinismo filtra información. - Es maleable. Por las propiedades de las potencias,
c · (2^e) mod ndescifra a2m: un atacante puede transformar el cifrado de forma predecible sin conocerlo. - Mensajes pequeños son frágiles. Con
epequeño y mensajes cortos sin relleno, hay ataques algebraicos directos.
La solución es el padding OAEP (Optimal Asymmetric Encryption Padding): antes de aplicar la operación RSA, el mensaje se expande con relleno aleatorio y estructurado. Resultado: cada cifrado es distinto aunque el mensaje se repita, la maleabilidad se detecta y los ataques algebraicos desaparecen. OAEP es a RSA lo que GCM era a AES: la forma correcta de usar la primitiva.
Veámoslo con el hilo del curso: la Clínica Sol envía a MediNube un aviso corto cifrado, sin clave compartida previa:
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
# MediNube generó su par en el apartado anterior y publicó clave_publica.
# La Clínica Sol solo necesita esa clave pública:
mensaje = b"Alta de paciente ana.perez confirmada - Clinica Sol"
oaep = padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()), # función de máscara interna
algorithm=hashes.SHA256(), # hash del esquema OAEP
label=None, # casi nunca se usa; déjalo a None
)
cifrado = clave_publica.encrypt(mensaje, oaep)
print(len(cifrado)) # 384 bytes: SIEMPRE el tamaño de n (3072 bits)
# Solo MediNube, con la privada, puede descifrar:
recuperado = clave_privada.decrypt(cifrado, oaep)
print(recuperado.decode())Puntos a interiorizar:
- El padding se pasa explícitamente en cada
encrypt/decrypt, y debe ser el mismo en ambos lados. Enpyca/cryptographyno existe un "RSA sin padding" accesible por accidente — bien. - Cada ejecución produce un cifrado distinto (por el relleno aleatorio). Ejecuta
encryptdos veces con el mismo mensaje y compara: no coinciden, y ambos descifran bien. - El cifrado ocupa siempre el tamaño del módulo (384 bytes con clave de 3072), da igual que el mensaje tenga 5 bytes o 300.
- Existe un padding más antiguo, PKCS#1 v1.5, aún presente en protocolos heredados. Para cifrar, tiene un historial de ataques (Bleichenbacher, 1998, y sus reencarnaciones): en código nuevo, OAEP siempre.
Los límites fundamentales de RSA: tamaño y velocidad
Ya viste en el juguete que RSA cifra "un número menor que n". Con OAEP, el relleno consume parte de ese espacio; el máximo real es tamaño_clave - 2·tamaño_hash - 2 bytes:
maximo = clave_publica.key_size // 8 - 2 * (256 // 8) - 2
print(maximo) # 318 bytes con clave de 3072 y OAEP-SHA256
historial_completo = b"X" * 50_000 # un historial médico real
try:
clave_publica.encrypt(historial_completo, oaep)
except ValueError as e:
print("FALLA:", e) # el mensaje no cabeY además de pequeño, es lento. Comparado con la criptografía simétrica del módulo 2:
| RSA-3072 + OAEP | AES-256-GCM | |
|---|---|---|
| Tamaño de clave | 3072 bits (y crece mal: 128 bits de seguridad exigen 3072; 256 exigirían ~15360) | 256 bits |
| Mensaje máximo por operación | 318 bytes | En la práctica, gigabytes |
| Velocidad de cifrado | Miles de operaciones/s (descifrar, aún menos) | Gigabytes por segundo con AES-NI: miles de veces más rápido |
| Expansión del cifrado | Todo mensaje se convierte en 384 bytes | mensaje + 12 (nonce) + 16 (tag) |
| Necesita secreto compartido previo | No | Sí |
Conclusión que debes retener: RSA no sirve para cifrar datos; sirve para cifrar claves. El patrón universal es usar la asimétrica para hacer llegar una clave simétrica pequeña, y la simétrica (AES-GCM) para los datos de verdad. Ese patrón — el cifrado híbrido — es exactamente la lección 04-05, donde MediNube enviará exports completos de historiales combinando lo mejor de ambos mundos. Por ahora, quédate con la restricción.
Serialización de claves: el formato PEM
Un objeto RSAPrivateKey vive en la memoria de un proceso Python. Para guardarlo en disco, enviar la pública a una clínica o cargarlo al arrancar, hay que serializarlo. El formato universal es PEM: los bytes de la clave codificados en Base64 entre cabeceras -----BEGIN...----- (recuerda 01-02: Base64 codifica, no cifra — por eso la privada, además, se cifra con una passphrase).
from cryptography.hazmat.primitives import serialization
# --- Clave PRIVADA: SIEMPRE cifrada con passphrase al guardarla ---
pem_privada = clave_privada.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8, # el formato moderno estándar
encryption_algorithm=serialization.BestAvailableEncryption(
b"passphrase-larga-de-verdad-no-esta" # en real: de un gestor de secretos
),
)
print(pem_privada.decode()[:70]) # -----BEGIN ENCRYPTED PRIVATE KEY-----
# --- Clave PÚBLICA: sin cifrar, es pública ---
pem_publica = clave_publica.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo, # el estándar
)
print(pem_publica.decode()[:30]) # -----BEGIN PUBLIC KEY-----
# --- Cargar de vuelta ---
privada2 = serialization.load_pem_private_key(
pem_privada, password=b"passphrase-larga-de-verdad-no-esta"
)
publica2 = serialization.load_pem_public_key(pem_publica)PKCS8es el formato contenedor moderno para privadas (verás también el heredado "TraditionalOpenSSL"; para código nuevo, PKCS8).BestAvailableEncryption(passphrase)cifra el PEM privado con lo mejor que ofrezca la librería. Una privada en disco sin cifrar es un secreto desnudo: quien lea el fichero es MediNube. La passphrase, como aprendiste en 02-04, pasa por una KDF antes de convertirse en clave — la librería lo hace por ti.SubjectPublicKeyInfoes el formato estándar de públicas; su nombre viene del mundo de los certificados X.509, que pisaremos en el módulo 5.- Dónde guardar el PEM privado y su passphrase en producción (permisos, gestor de secretos, HSM, rotación) es materia del módulo 6; aquí la disciplina mínima: privada cifrada, passphrase fuera del repositorio.
La pregunta incómoda: ¿de quién es esta clave pública?
Cerremos con el cabo suelto más importante. El flujo completo fue: MediNube genera su par, publica pem_publica, y la Clínica Sol cifra hacia esa clave. Pero ponte en la piel del desarrollador de la Clínica Sol que recibe por correo un fichero medinube_publica.pem. ¿Cómo sabe que esa clave es realmente de MediNube?
Un atacante en la red (o que compromete el correo) puede interceptar el PEM y sustituirlo por su propia clave pública. La clínica cifraría hacia el atacante, este descifraría, leería el historial, lo recifraría con la pública auténtica de MediNube y lo reenviaría. Nadie notaría nada: el cifrado "funciona" en ambos tramos. La confidencialidad matemática es perfecta; la identidad de la clave es el eslabón roto.
La criptografía asimétrica resuelve la distribución de claves de cifrado... y a cambio crea el problema de la distribución autenticada de claves públicas. Este problema tiene solución — certificados y autoridades de certificación — pero requiere piezas que aún no tenemos, así que lo dejamos explícitamente abierto hasta el módulo 5. Lo volveremos a ver, con más dramatismo, en 04-04.
Errores Comunes y Consejos
- Confundir el sentido de las claves. Se cifra con la pública del destinatario (no con la tuya) y se descifra con la privada propia. Si te descubres cifrando con una privada, o estás firmando (04-03) o estás confundido.
- Implementar RSA a mano "porque las matemáticas son sencillas". El juguete de esta lección omite generación segura de primos, padding, protección contra timing attacks (recuerda 01-04) y una docena de cosas más. Regla de oro 1: usa la librería.
- Claves de 1024 bits (o menos) en código heredado. Si auditando MediNube encuentras
key_size=1024, es un hallazgo de seguridad: factorizable con recursos serios. Mínimo 2048, recomendado 3072+. - Cifrar con PKCS#1 v1.5 en código nuevo. Es el padding heredado con historial de ataques. Para cifrar, OAEP siempre.
- Guardar la privada en PEM sin passphrase (
NoEncryption()). Hay casos legítimos (la passphrase la aporta un gestor de secretos externo), pero por defecto:BestAvailableEncryption. - Intentar cifrar datos grandes con RSA troceándolos. Cifrar un fichero en bloques de 318 bytes con RSA es lentísimo y criptográficamente delicado. El patrón correcto es el híbrido: paciencia hasta 04-05.
- Dar por buena una clave pública recibida por un canal inseguro. Acabas de ver por qué. Hasta el módulo 5, al menos verifica su huella (hash SHA-256 del PEM, como en 03-01) por un segundo canal.
Ejercicios
-
Juguete completo. Con
p = 11,q = 19ye = 7, calcula a mano (con ayuda de Python parapow) los valoresn,phiyd; cifra el mensajem = 8y comprueba que descifra bien. ¿Por qué un atacante que ven = 209ye = 7tarda segundos en romperlo, y con unnde 3072 bits no puede? -
El pecado del determinismo. Escribe una función
adivinar_resultado_MAL(cifrado, clave_publica)que demuestre el ataque al RSA de libro de texto: usando el RSA de juguete (sin padding), y sabiendo que el mensaje solo puede ser1("POSITIVO") o0("NEGATIVO"), recupera el mensaje sin conocer la clave privada. Explica por qué OAEP hace imposible este ataque. -
Intercambio de PEMs. Simula el arranque de la integración MediNube ↔ Clínica Sol: (a) genera el par RSA-3072 de MediNube y serialízalo (privada con passphrase, pública en claro); (b) "envía" la pública a la Clínica Sol (una variable), que la carga con
load_pem_public_keyy cifra con OAEP el mensajeb"Solicitud de acceso al historial de ana.perez"; (c) MediNube carga su privada desde el PEM con la passphrase y descifra. (d) Calcula el máximo de bytes cifrables y comprueba contry/exceptqué pasa al cifrar un mensaje de 400 bytes.
Soluciones
p, q, e = 11, 19, 7 n = p * q # 209 phi = (p - 1) * (q - 1) # 180 d = pow(e, -1, phi) # 103 (7·103 = 721 = 4·180 + 1) c = pow(8, e, n) # 8^7 mod 209 = 137 assert pow(c, d, n) == 8 # 137^103 mod 209 = 8 ✔
Con n = 209, el atacante prueba divisores y factoriza al instante (209 = 11 × 19), reconstruye phi = 180 y calcula d igual que el dueño. Con 3072 bits, la factorización no es "más lenta": es inviable — el coste crece de forma superpolinómica con el tamaño, y no existe algoritmo clásico eficiente. La seguridad de RSA es exactamente la dureza de ese problema.
def adivinar_resultado_MAL(cifrado, n, e):
# El atacante cifra los DOS candidatos con la clave pública y compara.
for candidato in (0, 1):
if pow(candidato, e, n) == cifrado:
return "POSITIVO" if candidato == 1 else "NEGATIVO"
c = pow(1, 7, 209) # la clínica cifra "POSITIVO"
print(adivinar_resultado_MAL(c, 209, 7)) # POSITIVO — sin tocar la privadaEl ataque funciona porque el RSA de libro de texto es determinista: cifrar el mismo mensaje da siempre el mismo resultado, y el atacante puede cifrar (la clave es pública). OAEP añade relleno aleatorio en cada cifrado: los dos cifrados de un mismo candidato nunca coinciden, así que comparar no dice nada. (De propina: pow(0, e, n) == 0 y pow(1, e, n) == 1 — ¡el libro de texto ni siquiera oculta el 0 y el 1!)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
# (a) MediNube
priv = rsa.generate_private_key(public_exponent=65537, key_size=3072)
pem_priv = priv.private_bytes(
serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8,
serialization.BestAvailableEncryption(b"frase-de-medinube"))
pem_pub = priv.public_key().public_bytes(
serialization.Encoding.PEM, serialization.PublicFormat.SubjectPublicKeyInfo)
# (b) Clínica Sol
oaep = padding.OAEP(mgf=padding.MGF1(hashes.SHA256()),
algorithm=hashes.SHA256(), label=None)
pub_clinica = serialization.load_pem_public_key(pem_pub)
cifrado = pub_clinica.encrypt(b"Solicitud de acceso al historial de ana.perez", oaep)
# (c) MediNube
priv2 = serialization.load_pem_private_key(pem_priv, password=b"frase-de-medinube")
print(priv2.decrypt(cifrado, oaep))
# (d) Límite
print(3072 // 8 - 2 * 32 - 2) # 318 bytes
try:
pub_clinica.encrypt(b"X" * 400, oaep)
except ValueError as e:
print("FALLA:", e) # Data too long / Encryption failedEl paso (b) contiene la trampa conceptual del ejercicio: la Clínica Sol acepta pem_pub sin verificar de quién es. Funciona, pero es el agujero del último apartado — módulo 5.
Conclusión
Has cruzado la frontera conceptual del curso. La criptografía asimétrica sustituye el secreto compartido por un par de claves: la privada nunca sale de casa; la pública se reparte sin miedo. Su fundamento son las funciones de un solo sentido con trampilla — para RSA, multiplicar primos es fácil y factorizar el producto es inviable —, y lo has visto por dentro con un RSA de juguete (d = pow(e, -1, phi)) y por fuera con el real: rsa.generate_private_key (3072 bits recomendado), padding OAEP obligatorio porque el RSA "de libro de texto" es determinista y maleable, y serialización PEM con la privada siempre cifrada (BestAvailableEncryption). También conoces sus dos límites: solo cifra mensajes diminutos (318 bytes con clave de 3072) y es miles de veces más lento que AES — RSA cifra claves, no datos (el híbrido llega en 04-05) —, y arrastra una pregunta sin responder: ¿cómo sabes de quién es una clave pública? (módulo 5). Pero antes de resolver nada de eso hay que hablar de un problema práctico: las claves RSA son enormes y crecen fatal — para 256 bits de seguridad harían falta ~15360 bits de clave. Existe otra familia de trampillas que da las mismas garantías con claves de solo 256 bits, y es la que domina la criptografía moderna, de tu móvil a TLS. Es la criptografía de curva elíptica, y te espera en la próxima lección, 04-02. Nos vemos allí.
Curso de Criptografía Aplicada
Módulo 1: Fundamentos de la Criptografía
- Qué es la criptografía y para qué sirve
- Codificación, ofuscación y cifrado
- Aleatoriedad y entropía
- El principio de Kerckhoffs y las reglas de oro
Módulo 2: Criptografía Simétrica
- Cifrado simétrico: AES y ChaCha20
- Modos de operación
- Cifrado autenticado (AEAD)
- Derivación de claves (KDF)
Módulo 3: Hashes, MAC y Contraseñas
- Funciones hash criptográficas
- Autenticación de mensajes con HMAC
- Almacenamiento seguro de contraseñas
Módulo 4: Criptografía Asimétrica
- Fundamentos de clave pública y RSA
- Criptografía de curva elíptica
- Firmas digitales
- Intercambio de claves: Diffie-Hellman
- Cifrado híbrido
Módulo 5: PKI, Certificados y TLS
- Certificados X.509 y autoridades de certificación
- TLS en la práctica
- Gestión del ciclo de vida de certificados
