Clave Pública y Clave Privada
La criptografía asimétrica es la tecnología que hace posible Internet seguro: HTTPS, SSH, las firmas digitales de tu DNI electrónico, los certificados de los bancos y las claves GPG. Se basa en un principio matemático elegante: dos claves distintas pero relacionadas matemáticamente, donde lo que cifra una solo puede descifrarlo la otra.
La idea central en una analogía
La cerradura
echar un mensaje
La llave
abrir el buzón
Imagina un buzón con una ranura y una cerradura. Cualquier persona puede meter un sobre por la ranura (usando tu clave pública), pero solo el dueño de la llave (clave privada) puede abrir el buzón y leer los sobres. La clave pública cifra; la clave privada descifra.
¿Dónde usas criptografía asimétrica cada día?
| Tecnología | Algoritmo usado | Para qué lo usa |
|---|---|---|
| HTTPS (candado del navegador) | RSA o ECDSA + ECDH | Autenticar el servidor y negociar la clave de sesión |
| SSH (acceso remoto a servidores) | RSA, Ed25519, ECDSA | Autenticar sin contraseña mediante par de claves |
| GPG / Email cifrado | RSA o ElGamal | Cifrar emails y firmarlos digitalmente |
| DNI electrónico | RSA-2048 | Firma digital con validez legal |
| Bitcoin / Blockchain | ECDSA (secp256k1) | Demostrar propiedad de una dirección sin revelar la clave |
| JWT (tokens de autenticación web) | RSA o ECDSA | Firmar tokens para que no puedan falsificarse |
| Signal / WhatsApp | Curve25519 (DH) | Cifrado de extremo a extremo con forward secrecy |
Existen dos grandes familias de criptografía. Entender las diferencias explica por qué la asimétrica no ha reemplazado a la simétrica — sino que se usan juntas.
| Característica | Simétrica (AES, ChaCha20) | Asimétrica (RSA, ECC) |
|---|---|---|
| Número de claves | Una sola clave compartida | Par de claves: pública + privada |
| Problema fundamental | ¿Cómo compartir la clave de forma segura? | No hay problema de distribución — la pública es pública |
| Velocidad | Muy rápida (hardware dedicado) | Lenta (operaciones matemáticas costosas) |
| Longitud de clave | 128-256 bits es suficiente | 2048-4096 bits para RSA; 256-521 bits para ECC |
| Cifrar datos grandes | Ideal | No práctico (lento y limita tamaño) |
| Firmas digitales | No directamente | Función nativa |
| Sin contacto previo | Imposible sin canal seguro | Posible — solo necesitas la clave pública |
AES temporal
con RSA pública del servidor
con RSA privada
cifrados con AES
(rápido)
El par de claves es el concepto central de toda la criptografía asimétrica. Entender qué hace cada una, qué nunca debe hacer cada una, y cómo se relacionan matemáticamente.
Las dos claves y sus roles
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2a2rwplBQLF29amygykE
MmYz0+Kcj3bKBp29SkH1lMiE0VQT1p0YhJDrqEFqGZi5BO1QQWPGB7FcjfxGhL
-----END PUBLIC KEY-----
✓ Publicar en servidores de claves ✓ Incluir en emails ✓ Poner en tu web ✓ Compartir con todo el mundo
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDZravCmUFAsXb1
qbKDKQQyZjPT4pyPdsoGnb1KQfWUyITRVBPWnRiEkOuoQWoZmLkE7VBBY8YHsV
-----END PRIVATE KEY-----
✗ Nunca enviar por email ✗ Nunca subir a GitHub ✗ Nunca compartir con nadie
✓ Guardar cifrada con passphrase ✓ Backup seguro y offline
Las cuatro operaciones fundamentales
| Operación | Quién usa qué clave | Para qué sirve | Ejemplo |
|---|---|---|---|
| Cifrar | Remitente usa la clave pública del destinatario | Solo el destinatario puede leerlo | Alice cifra un mensaje con la clave pública de Bob |
| Descifrar | Destinatario usa su clave privada | Recuperar el mensaje original | Bob descifra con su clave privada |
| Firmar | Firmante usa su clave privada | Demostrar autoría e integridad | Alice firma un contrato con su clave privada |
| Verificar firma | Cualquiera usa la clave pública del firmante | Confirmar que fue firmado por el dueño | Bob verifica la firma con la clave pública de Alice |
RSA (Rivest-Shamir-Adleman, 1977) es el algoritmo de clave pública más conocido. Su seguridad se basa en la dificultad matemática de factorizar el producto de dos números primos grandes. Entender las matemáticas desmitifica el algoritmo.
El problema difícil detrás de RSA
Multiplicar dos números primos es trivial. Pero dado su producto, encontrar de vuelta los dos primos originales es computacionalmente inviable para números grandes.
Generación de claves RSA — los 5 pasos
Usando el par de claves generado en la sección anterior (n=3233, e=17, d=2753), vamos a cifrar y descifrar un mensaje real paso a paso.
Cifrar con la clave pública
Para cifrar un número M (el mensaje), aplicamos la fórmula: C = Me mod n. El resultado C es el texto cifrado.
Descifrar con la clave privada
Para descifrar C con la clave privada, aplicamos: M = Cd mod n. La magia matemática del teorema de Euler garantiza que esto devuelve M exactamente.
Verificar con Python
# RSA desde cero con los parámetros del ejemplo # Puedes ejecutar esto en cualquier Python 3 ## PASO 1: Definir los primos p, q = 61, 53 ## PASO 2: Calcular n y phi(n) n = p * q # 3233 phi = (p-1) * (q-1)# 3120 ## PASO 3: Clave pública (e coprimo con phi) e = 17 from math import gcd assert gcd(e, phi) == 1, "e no es coprimo con phi" ## PASO 4: Clave privada (d inverso modular de e) d = pow(e, -1, phi) # Python 3.8+ calcula el inverso modular directamente print(f"Clave pública: (n={n}, e={e})") print(f"Clave privada: (n={n}, d={d})") Clave pública: (n=3233, e=17) Clave privada: (n=3233, d=2753) ## CIFRAR: C = M^e mod n M = 65 # mensaje C = pow(M, e, n) # pow(base, exp, mod) es eficiente para números grandes print(f"Mensaje original: {M}") print(f"Mensaje cifrado: {C}") Mensaje original: 65 Mensaje cifrado: 2790 ## DESCIFRAR: M = C^d mod n M_dec = pow(C, d, n) print(f"Mensaje descifrado: {M_dec}") assert M_dec == M, "¡Error de descifrado!" Mensaje descifrado: 65 ← ¡correcto! ## VERIFICAR que sin d no se puede descifrar # Sin conocer p y q, encontrar d requiere factorizar n # Para n=3233 es fácil, pero para RSA-2048 es computacionalmente imposible
Dejar los números pequeños y trabajar con RSA real: generar un par de claves de 2048 bits, cifrar un mensaje y descifrarlo usando las herramientas estándar de la industria.
Con OpenSSL — La herramienta estándar
## PASO 1: Generar la clave privada RSA-2048 openssl genrsa -out clave_privada.pem 2048 Generating RSA private key, 2048 bit long modulus (2 primes) .........+++++ .....+++++ e is 65537 (0x10001) ## PASO 2: Extraer la clave pública de la privada openssl rsa -in clave_privada.pem -pubout -out clave_publica.pem writing RSA key ## Ver la clave pública generada cat clave_publica.pem -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2a2rwplBQLzskS6ACJM5 ... -----END PUBLIC KEY----- ## Ver los componentes matemáticos de la clave openssl rsa -in clave_privada.pem -text -noout | head -20 Private-Key: (2048 bit, 2 primes) modulus: 00:d9:ad:ab:c2:99:41:40:bc:... ← n (2048 bits) publicExponent: 65537 (0x10001) ← e = 65537 privateExponent: ← d (secreto) ... prime1: ← p prime2: ← q ## PASO 3: Cifrar un mensaje con la clave pública echo "Hola GhostAcademy, esto es un secreto!" > mensaje.txt openssl rsautl -encrypt -inkey clave_publica.pem -pubin \ -in mensaje.txt -out mensaje_cifrado.bin ## Ver el contenido cifrado (binario ilegible) xxd mensaje_cifrado.bin | head -5 00000000: 3a8f d291 4c7a 2b88 e931 f450 a2b3 ... ← ilegible sin la clave privada ## PASO 4: Descifrar con la clave privada openssl rsautl -decrypt -inkey clave_privada.pem \ -in mensaje_cifrado.bin -out mensaje_descifrado.txt cat mensaje_descifrado.txt Hola GhostAcademy, esto es un secreto! ## CIFRADO HÍBRIDO PRÁCTICO — RSA + AES (lo que se usa en producción) # Cifrar un archivo grande (RSA tiene límite de tamaño) # 1. Generar clave AES aleatoria openssl rand -base64 32 > clave_aes.key # 2. Cifrar el archivo grande con AES openssl enc -aes-256-cbc -salt -in archivo_grande.txt \ -out archivo_cifrado.enc -pass file:clave_aes.key # 3. Cifrar la clave AES con RSA (esto es lo que se envía al destinatario) openssl rsautl -encrypt -inkey clave_publica.pem -pubin \ -in clave_aes.key -out clave_aes_cifrada.bin # Enviar al destinatario: archivo_cifrado.enc + clave_aes_cifrada.bin # El destinatario descifra la clave AES con su RSA privada, luego descifra el archivo
Con Python — cryptography library
# Instalar: pip install cryptography from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import hashes, serialization ## GENERAR PAR DE CLAVES RSA-2048 clave_privada = rsa.generate_private_key( public_exponent=65537, # e estándar key_size=2048, # tamaño de n en bits ) clave_publica = clave_privada.public_key() ## CIFRAR con la clave pública (OAEP es el padding recomendado) mensaje = b"Hola GhostAcademy, esto es secreto!" cifrado = clave_publica.encrypt( mensaje, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) print(f"Mensaje cifrado: {cifrado.hex()[:60]}...") Mensaje cifrado: 3a8fd2914c7a2b88e931f450a2b3c8d1e4f6a7b8c9d0e1f2... ## DESCIFRAR con la clave privada descifrado = clave_privada.decrypt( cifrado, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) print(f"Mensaje descifrado: {descifrado.decode()}") Mensaje descifrado: Hola GhostAcademy, esto es secreto! ## EXPORTAR CLAVES en formato PEM pem_privada = clave_privada.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.BestAvailableEncryption(b"mi_passphrase") ) pem_publica = clave_publica.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ) with open("privada.pem", "wb") as f: f.write(pem_privada) with open("publica.pem", "wb") as f: f.write(pem_publica) print("Claves guardadas en privada.pem y publica.pem")
Las firmas digitales son la operación inversa al cifrado: si cifrar usa la clave pública del destinatario, firmar usa la clave privada propia. Esto permite probar autoría e integridad sin revelar el contenido.
¿Qué garantiza una firma digital?
| Propiedad | Significado | Cómo se consigue |
|---|---|---|
| Autenticidad | El documento fue creado por quien dice haberlo firmado | Solo el dueño de la clave privada puede crear la firma |
| Integridad | El documento no fue modificado después de firmarse | El hash del documento forma parte de la firma |
| No repudio | El firmante no puede negar haber firmado | Solo él tenía la clave privada en el momento de la firma |
El proceso completo de firma y verificación
tiene el documento
hash
a3f8c2...
(hash cifrado)
original
ambos
recibido
calculado
la firma
→ firma válida
→ documento alterado
Firma RSA en números — usando nuestras claves del ejemplo
Firmar y verificar documentos reales usando OpenSSL y Python. Estas son las operaciones exactas que ocurren cuando se firman contratos digitales, commits de Git o paquetes de software.
## PREPARAR: reutilizamos las claves generadas antes echo "Este contrato es vinculante. Fecha: $(date)" > contrato.txt cat contrato.txt Este contrato es vinculante. Fecha: Mon Jan 15 14:32:00 UTC 2024 ## FIRMAR el documento con la clave privada openssl dgst -sha256 -sign clave_privada.pem \ -out contrato.sig contrato.txt ## Ver la firma (binario codificado en hex) xxd contrato.sig | head -3 00000000: 7a3b c891 2e45 f7d8 9a12 ... ← firma de 256 bytes (RSA-2048) ## VERIFICAR la firma con la clave pública openssl dgst -sha256 -verify clave_publica.pem \ -signature contrato.sig contrato.txt Verified OK ← firma válida ## QUÉ PASA si el documento es modificado después de firmar echo "clausula_maliciosa" >> contrato.txt openssl dgst -sha256 -verify clave_publica.pem \ -signature contrato.sig contrato.txt Verification Failure ← ¡el documento fue alterado! la firma no coincide ## FIRMAR CON ECDSA (más eficiente que RSA para firmas) openssl ecparam -name prime256v1 -genkey -noout -out ec_privada.pem openssl ec -in ec_privada.pem -pubout -out ec_publica.pem openssl dgst -sha256 -sign ec_privada.pem -out contrato_ec.sig contrato.txt openssl dgst -sha256 -verify ec_publica.pem -signature contrato_ec.sig contrato.txt Verified OK
from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import hashes, serialization ## Generar par de claves clave_privada = rsa.generate_private_key(public_exponent=65537, key_size=2048) clave_publica = clave_privada.public_key() ## Documento a firmar documento = b"Este contrato tiene validez legal. Firmado por: Alice" ## FIRMAR con la clave privada (PSS es el padding recomendado para firmas) firma = clave_privada.sign( documento, padding.PSS( mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA256() ) print(f"Firma generada: {firma.hex()[:60]}...") ## VERIFICAR con la clave pública try: clave_publica.verify( firma, documento, padding.PSS( mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA256() ) print("✓ Firma válida — el documento es auténtico e íntegro") except Exception as e: print(f"✗ Firma inválida: {e}") ## Qué ocurre con documento alterado documento_alterado = b"Este contrato tiene validez legal. Firmado por: Bob" try: clave_publica.verify(firma, documento_alterado, padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH), hashes.SHA256()) except Exception: print("✗ Firma inválida — el documento fue modificado") Firma generada: 7a3bc8912e45f7d89a12... ✓ Firma válida — el documento es auténtico e íntegro ✗ Firma inválida — el documento fue modificado
Elliptic Curve Cryptography (ECC) es la alternativa moderna a RSA. Proporciona el mismo nivel de seguridad con claves mucho más cortas, lo que la hace más eficiente en dispositivos móviles, IoT y conexiones TLS modernas.
RSA vs ECC — Comparativa de tamaño de clave
| Nivel de seguridad | Clave RSA necesaria | Clave ECC equivalente | Ratio |
|---|---|---|---|
| 80 bits | 1024 bits | 160 bits | 6:1 |
| 112 bits | 2048 bits | 224 bits | 9:1 |
| 128 bits | 3072 bits | 256 bits | 12:1 |
| 192 bits | 7680 bits | 384 bits | 20:1 |
| 256 bits | 15360 bits | 521 bits | 30:1 |
El problema difícil detrás de ECC
ECC se basa en el problema del logaritmo discreto en curvas elípticas. Una curva elíptica es el conjunto de puntos que satisfacen: y² = x³ + ax + b (mod p). Se puede "sumar" puntos de la curva de forma eficiente, pero dado un punto resultado P = n×G, encontrar n conociendo P y G es computacionalmente inviable.
Curvas elípticas más usadas
| Curva | Bits | Dónde se usa |
|---|---|---|
| prime256v1 / P-256 | 256 | TLS, ECDSA en certificados web, Android, Apple |
| secp384r1 / P-384 | 384 | TLS de alta seguridad, gobierno, NSA Suite B |
| secp256k1 | 256 | Bitcoin, Ethereum (no es la P-256 estándar de NIST) |
| Curve25519 / Ed25519 | 255 | SSH moderno, Signal, WireGuard, TLS 1.3 |
| X448 | 448 | TLS 1.3 de alta seguridad |
## OPENSSL — Generar clave ECC (Ed25519 — la más moderna) openssl genpkey -algorithm Ed25519 -out ed25519_privada.pem openssl pkey -in ed25519_privada.pem -pubout -out ed25519_publica.pem ## Comparar tamaños con RSA wc -c ed25519_privada.pem rsa_privada.pem 119 ed25519_privada.pem ← Ed25519: 119 bytes 1704 rsa_privada.pem ← RSA-2048: 1704 bytes ← 14x más grande ## PYTHON — ECC con Ed25519 from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey privada = Ed25519PrivateKey.generate() publica = privada.public_key() mensaje = b"Firmado con curva elíptica Ed25519" firma = privada.sign(mensaje) # Verificar publica.verify(firma, mensaje) print("✓ Firma Ed25519 válida") # Ed25519 es más rápido que RSA y más seguro a igual tamaño de clave ✓ Firma Ed25519 válida
Diffie-Hellman (1976) resolvió un problema fundamental: ¿cómo pueden dos personas acordar una clave secreta a través de un canal inseguro, sin haberse comunicado antes? Es el fundamento del "Perfect Forward Secrecy" en TLS moderno.
La analogía de los colores
La forma más intuitiva de entender DH: Alice y Bob mezclan colores en público, pero ningún observador puede saber el color secreto final aunque vea todos los intercambios.
AMARILLO
(g y p son públicos)
ROJO (secreto)
AZUL (secreto)
NARANJA
(amarillo+rojo)
resultado MARRÓN
resultado MARRÓN
VERDE
(amarillo+azul)
Diffie-Hellman en matemáticas
TLS (Transport Layer Security) es el protocolo detrás del candado verde del navegador. Combina criptografía asimétrica para autenticación e intercambio de claves, con criptografía simétrica para los datos. Entender TLS es entender la criptografía asimétrica en acción.
El TLS Handshake — paso a paso
(navegador)
(banco.com)
Calcula secreto DH
Deriva claves AES
Deriva claves AES
Qué papel juega cada tipo de criptografía
| Fase del handshake | Algoritmo | Tipo | Para qué |
|---|---|---|---|
| Verificar identidad del servidor | RSA o ECDSA | Asimétrica | El servidor firma con su clave privada; el cliente verifica con la pública del certificado |
| Intercambio de clave de sesión | ECDH (Elliptic Curve DH) | Asimétrica | Cliente y servidor derivan el mismo secreto sin enviarlo nunca — Perfect Forward Secrecy |
| Cifrar la comunicación | AES-256-GCM | Simétrica | Cifrado rápido de todos los datos HTTP con la clave derivada |
| Integridad del mensaje | SHA-256/384 (HMAC) | Hash | Verificar que ningún byte fue alterado en tránsito |
# Ver el certificado completo de un servidor echo | openssl s_client -connect google.com:443 2>/dev/null | openssl x509 -noout -text | grep -E "Subject|Issuer|Algorithm|Public Key|Not|SANs" Subject: CN=*.google.com Issuer: C=US, O=Google Trust Services, CN=WR2 Signature Algorithm: ecdsa-with-SHA384 Public Key Algorithm: id-ecPublicKey (P-256) Not Before: Jan 10 00:00:00 2024 GMT Not After : Apr 3 23:59:59 2024 GMT # Ver qué cipher suite se negoció echo | openssl s_client -connect google.com:443 2>/dev/null | grep "Cipher" New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 ← TLS 1.3 + AES-256-GCM para datos + SHA-384 para integridad # Ver la clave pública del certificado echo | openssl s_client -connect github.com:443 2>/dev/null | openssl x509 -noout -pubkey -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE... ← clave pública ECDSA P-256 de GitHub -----END PUBLIC KEY-----
SSH y GPG son las dos herramientas de uso diario que más directamente exponen la criptografía asimétrica al usuario. SSH para autenticación sin contraseña en servidores; GPG para cifrar y firmar emails, archivos y commits de Git.
SSH — Autenticación con clave pública
## GENERAR PAR DE CLAVES SSH # Ed25519 — recomendado (ECC moderno, clave pequeña y segura) ssh-keygen -t ed25519 -C "tu_email@ejemplo.com" -f ~/.ssh/id_ed25519 Generating public/private ed25519 key pair. Enter passphrase: **** ← proteger la clave privada con passphrase Your identification has been saved in /home/user/.ssh/id_ed25519 Your public key has been saved in /home/user/.ssh/id_ed25519.pub # RSA-4096 — alternativa compatible con sistemas más antiguos ssh-keygen -t rsa -b 4096 -C "tu_email@ejemplo.com" -f ~/.ssh/id_rsa ## VER LAS CLAVES cat ~/.ssh/id_ed25519.pub ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... tu_email@ejemplo.com ← esta es la clave PÚBLICA — puedes copiarla a cualquier servidor ## COPIAR LA CLAVE PÚBLICA AL SERVIDOR ssh-copy-id -i ~/.ssh/id_ed25519.pub usuario@servidor.com # Esto añade tu clave pública al ~/.ssh/authorized_keys del servidor ## CONECTAR SIN CONTRASEÑA (usando la clave privada) ssh -i ~/.ssh/id_ed25519 usuario@servidor.com ## QUÉ OCURRE DURANTE LA AUTENTICACIÓN SSH 1. El servidor genera un número aleatorio y lo cifra con tu clave pública 2. Te lo envía cifrado — solo tu clave privada puede descifrarlo 3. Tu cliente descifra el desafío y envía la respuesta 4. El servidor verifica que la respuesta es correcta → autenticado ✓ (nunca se envía la clave privada ni la contraseña por la red) ## FIRMAR COMMITS DE GIT CON SSH (GitHub, GitLab) git config --global gpg.format ssh git config --global user.signingkey ~/.ssh/id_ed25519.pub git commit -S -m "commit firmado con mi clave SSH"
GPG — Cifrar y firmar archivos y emails
## GENERAR PAR DE CLAVES GPG gpg --full-generate-key # Seleccionar: RSA and RSA, 4096 bits, sin expiración (o con) # Introducir nombre y email, proteger con passphrase ## VER LAS CLAVES GENERADAS gpg --list-keys pub rsa4096 2024-01-15 [SC] A1B2C3D4E5F67890A1B2C3D4E5F67890A1B2C3D4 uid [ultimate] Tu Nombre <tu@email.com> sub rsa4096 2024-01-15 [E] ## EXPORTAR LA CLAVE PÚBLICA (para compartir) gpg --armor --export tu@email.com > mi_clave_publica.asc cat mi_clave_publica.asc -----BEGIN PGP PUBLIC KEY BLOCK----- mQINBGb... -----END PGP PUBLIC KEY BLOCK----- ## IMPORTAR LA CLAVE PÚBLICA DE OTRA PERSONA gpg --import clave_publica_bob.asc ## CIFRAR un archivo para Bob (solo Bob puede descifrarlo) echo "Mensaje secreto para Bob" > secreto.txt gpg --encrypt --recipient bob@email.com --armor -o secreto.gpg secreto.txt ## DESCIFRAR (Bob lo ejecuta con su clave privada) gpg --decrypt secreto.gpg gpg: encrypted with RSA key, ID ... Mensaje secreto para Bob ## FIRMAR un archivo (demostrar autoría) gpg --sign --armor -o contrato.sig contrato.txt ## VERIFICAR la firma gpg --verify contrato.sig contrato.txt gpg: Good signature from "Tu Nombre <tu@email.com>" [ultimate] ## CIFRAR Y FIRMAR a la vez (lo más común) gpg --encrypt --sign --recipient bob@email.com --armor -o mensaje.gpg mensaje.txt ## FIRMAR COMMITS DE GIT CON GPG git config --global user.signingkey TU_ID_GPG git config --global commit.gpgsign true git commit -m "commit con firma GPG automática"
JSON Web Tokens (JWT) son el estándar para tokens de autenticación en APIs web modernas. La criptografía asimétrica permite que el servidor firme los tokens con su clave privada y cualquier servicio los verifique con la clave pública — sin necesitar una base de datos central de sesiones.
Estructura de un JWT
# Un JWT tiene 3 partes separadas por puntos, cada una en Base64URL: eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyMTIzIiwiZXhwIjoxNzAwMDAwMDAwfQ.FIRMA_AQUI # ↑ Header ↑ Payload ↑ Signature # HEADER (decodificado): {"alg": "RS256", "typ": "JWT"} ← RS256 = RSA + SHA-256 para la firma # PAYLOAD (decodificado): {"sub": "user123", "exp": 1700000000, "role": "admin"} ← claims: quién es el usuario y qué puede hacer # SIGNATURE: RSA_SIGN(SHA256(base64(header) + "." + base64(payload)), clave_privada_servidor) ← el servidor firma el header+payload — nadie puede falsificar el token sin la clave privada
# pip install pyjwt cryptography import jwt from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import serialization ## Generar par de claves RSA para el servidor de autenticación clave_privada = rsa.generate_private_key(public_exponent=65537, key_size=2048) clave_publica = clave_privada.public_key() ## SERVIDOR — Emitir token al hacer login payload = { "sub": "user123", "name": "Juan García", "role": "admin", "exp": 9999999999 # expiración } token = jwt.encode(payload, clave_privada, algorithm="RS256") print(f"Token JWT: {token[:80]}...") ## CUALQUIER MICROSERVICIO — Verificar el token con la clave pública try: datos = jwt.decode(token, clave_publica, algorithms=["RS256"]) print(f"✓ Token válido. Usuario: {datos['name']}, Rol: {datos['role']}") except jwt.InvalidTokenError as e: print(f"✗ Token inválido: {e}") ## Intentar falsificar el token (modificar el payload) import base64, json partes = token.split('.') payload_falso = base64.urlsafe_b64encode( json.dumps({"sub": "hacker", "role": "superadmin"}).encode() ).rstrip(b'=').decode() token_falso = f"{partes[0]}.{payload_falso}.{partes[2]}" try: jwt.decode(token_falso, clave_publica, algorithms=["RS256"]) except jwt.InvalidTokenError: print("✗ Token falsificado rechazado — la firma no coincide") Token JWT: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... ✓ Token válido. Usuario: Juan García, Rol: admin ✗ Token falsificado rechazado — la firma no coincide
Un certificado digital X.509 es una clave pública con una identidad firmada por una autoridad de confianza (CA). Es la respuesta al problema de "¿cómo sé que esta clave pública pertenece realmente a google.com y no a un atacante?".
El problema que resuelven los certificados
Si Alice publica su clave pública, ¿cómo sabe Bob que realmente es la de Alice y no la de un atacante que se hace pasar por ella? Los certificados X.509 añaden una firma de una tercera parte de confianza (la CA) que avala la identidad.
ISRG Root X1
(confiada por el SO)
Let's Encrypt R3
banco.com
clave pública del banco
Crear un certificado auto-firmado con OpenSSL
## En un solo comando: genera clave + CSR + certificado auto-firmado openssl req -x509 -newkey rsa:4096 -keyout clave_servidor.pem \ -out certificado.pem -days 365 -nodes \ -subj "/CN=miservidor.local/O=GhostAcademy/C=ES" ## Ver el contenido del certificado openssl x509 -in certificado.pem -text -noout | grep -E "Subject|Issuer|Validity|Public Key" Subject: CN=miservidor.local, O=GhostAcademy, C=ES Issuer: CN=miservidor.local, O=GhostAcademy, C=ES ← auto-firmado (sujeto = emisor) Validity: Not Before: Jan 15 14:32:00 2024 GMT Not After : Jan 15 14:32:00 2025 GMT Public Key Algorithm: rsaEncryption (4096 bit) ## Proceso real de solicitud a una CA (CSR) # 1. Generar clave privada openssl genrsa -out mi_dominio.key 2048 # 2. Crear la CSR (Certificate Signing Request) openssl req -new -key mi_dominio.key -out mi_dominio.csr \ -subj "/CN=mi-dominio.com/O=Mi Empresa/C=ES" # 3. Enviar mi_dominio.csr a la CA (Let's Encrypt, DigiCert...) # 4. La CA verifica que controlas el dominio y firma el certificado # 5. Recibes mi_dominio.crt firmado por la CA ## Let's Encrypt — certificado gratuito y automático sudo certbot --nginx -d mi-dominio.com # certbot gestiona todo: genera clave, envía CSR, obtiene cert, lo renueva
Resumen de todos los algoritmos de criptografía asimétrica cubiertos en el manual, con sus características, usos recomendados y estado de seguridad actual.
| Algoritmo | Tipo | Longitud clave | Usos principales | Estado |
|---|---|---|---|---|
| RSA-2048 | Cifrado + Firma | 2048 bits | TLS legacy, JWT RS256, S/MIME | Aceptable |
| RSA-4096 | Cifrado + Firma | 4096 bits | GPG, certificados de larga duración | Seguro |
| ECDSA P-256 | Solo Firma | 256 bits | TLS, certificados web, JWT ES256 | Recomendado |
| ECDSA P-384 | Solo Firma | 384 bits | Alta seguridad, gobierno | Recomendado |
| Ed25519 | Solo Firma | 255 bits | SSH moderno, Signal, WireGuard | ★ Mejor opción |
| ECDH / ECDHE | Intercambio de claves | 256-521 bits | TLS PFS, Signal Protocol | Recomendado |
| Diffie-Hellman | Intercambio de claves | 2048+ bits | TLS legacy (sin ECC) | Funcional |
| RSA-1024 | Cifrado + Firma | 1024 bits | — | ROTO — No usar |
| DSA | Solo Firma | 1024-3072 bits | — | Obsoleto — No usar |
¿Cuándo usar cada algoritmo?
| Necesito... | Usar | Por qué |
|---|---|---|
| Autenticación SSH | Ed25519 | Más rápido, clave más pequeña, resistente a ataques de canal lateral |
| Certificado TLS para web | ECDSA P-256 | Estándar moderno, soportado universalmente, perfecto forward secrecy |
| Firmar emails / archivos GPG | RSA-4096 o Ed25519 | RSA-4096 para máxima compatibilidad; Ed25519 para uso moderno |
| JWT para autenticación web | RS256 (RSA) o ES256 (ECDSA) | RS256 más compatible; ES256 más eficiente |
| Cifrar datos para alguien | RSA-OAEP o ECIES | Siempre cifrado híbrido — RSA cifra la clave simétrica, AES cifra los datos |
Comandos OpenSSL esenciales
## GENERAR CLAVES openssl genrsa -out priv.pem 2048 # RSA-2048 privada openssl rsa -in priv.pem -pubout -out pub.pem # extraer pública openssl genpkey -algorithm Ed25519 -out ed_priv.pem # Ed25519 privada openssl ecparam -name prime256v1 -genkey -out ec_priv.pem # ECDSA P-256 ## CIFRAR / DESCIFRAR (RSA) openssl rsautl -encrypt -inkey pub.pem -pubin -in msg.txt -out msg.enc openssl rsautl -decrypt -inkey priv.pem -in msg.enc ## FIRMAR / VERIFICAR openssl dgst -sha256 -sign priv.pem -out firma.sig archivo.txt openssl dgst -sha256 -verify pub.pem -signature firma.sig archivo.txt ## CERTIFICADOS openssl req -x509 -newkey rsa:2048 -keyout k.pem -out cert.pem -days 365 -nodes openssl x509 -in cert.pem -text -noout # ver certificado openssl verify -CAfile ca.pem cert.pem # verificar cadena ## INSPECCIONAR SERVIDOR TLS echo | openssl s_client -connect dominio.com:443 | openssl x509 -noout -text openssl s_client -connect dominio.com:443 -tls1_3 # forzar TLS 1.3 ## SSH ssh-keygen -t ed25519 -C "email" # generar par SSH ssh-copy-id usuario@servidor # copiar pública al servidor ssh-keygen -lf ~/.ssh/id_ed25519.pub # ver fingerprint
Conceptos clave para recordar
- La clave pública se comparte libremente — con ella cualquiera puede cifrar o verificar firmas
- La clave privada NUNCA se comparte — con ella descifras y firmas
- Cifrar: usa la clave pública del destinatario. Firmar: usa tu propia clave privada
- La criptografía asimétrica es lenta — en la práctica se usa solo para negociar claves simétricas (cifrado híbrido)
- RSA se basa en la dificultad de factorizar números grandes; ECC en el logaritmo discreto en curvas elípticas
- Una firma digital = hash del documento cifrado con la clave privada del firmante
- Un certificado X.509 = clave pública + identidad + firma de la CA
- Ed25519 es la mejor opción para firmas digitales hoy (SSH, Git, mensajería)
- Perfect Forward Secrecy: cada sesión TLS usa claves efímeras — comprometer la clave del servidor no descifra sesiones pasadas
- La criptografía post-cuántica (ML-KEM, ML-DSA) es el futuro — los algoritmos actuales son vulnerables a ordenadores cuánticos suficientemente grandes