Arribem a l'última lliçó del mòdul, i és la que lliga tots els caps. Al llarg del curs has acumulat dues famílies d'eines amb virtuts oposades: la criptografia simètrica (AES-GCM, ChaCha20) és rapidíssima i xifra gigabytes, però exigeix una clau compartida que no sabíem distribuir; la criptografia asimètrica (RSA, ECC) resol la distribució de claus, però és lenta i només maneja missatges diminuts. Cadascuna coixeja justament on l'altra brilla. La solució no és triar: és combinar-les. El patró s'anomena xifratge híbrid o sobre digital (envelope encryption), i és, sense exagerar, com es xifra de veritat al món real —des del TLS fins al xifratge de les teves còpies de seguretat al núvol. En aquesta lliçó el construiràs sencer: MediNube enviarà un export complet d'historials a la Clínica Sol, i veuràs les dues formes de "tancar el sobre" (RSA-OAEP clàssic i el patró modern amb X25519 efímer). Tancarem el mòdul amb un mapa de decisió de totes les primitives asimètriques i amb l'única peça que encara ens falta.
Contingut
- El dilema, recapitulat en una taula
- La idea del sobre digital
- Sobre digital amb RSA-OAEP (variant clàssica)
- Sobre digital amb X25519 efímer (variant moderna, ECIES-like)
- El format de sobre versionat de MediNube
- On viu aquest patró al món real
- Mapa de decisió del mòdul
El dilema, recapitulat en una taula
Posem cara a cara el que sabem, perquè l'híbrid neix exactament d'aquesta tensió:
| Simètrica (AES-256-GCM) | Asimètrica (RSA-OAEP / ECC) | |
|---|---|---|
| Velocitat | Gigabytes per segon (AES-NI) | Milers d'operacions/s: milers de vegades més lenta |
| Mida del missatge | A la pràctica, il·limitada | RSA: ≤318 bytes; ECC: no xifra dades directament |
| Distribució de claus | No resolta: exigeix secret compartit previ | Resolta: la pública es publica sense por |
| Punt fort | Xifrar les dades | Fer arribar una clau |
| Punt feble | Com arriba la clau? | Com xifro alguna cosa gran i ràpid? |
Llegeix l'última fila i veuràs que les debilitats encaixen com a peces de trencaclosques: el que la simètrica no sap fer (distribuir la clau), l'asimètrica ho fa de sobres; el que l'asimètrica no sap fer (xifrar dades grans i ràpid), la simètrica ho fa de sobres. La conclusió que ja anticipàvem a 04-01 —"l'RSA xifra claus, no dades"— es converteix aquí en una arquitectura.
La idea del sobre digital
El xifratge híbrid fa servir cada primitiva per al que sap fer:
- Es genera una clau de sessió simètrica aleatòria (una clau AES-256 fresca, del CSPRNG —regla d'or 3), d'usar i llençar.
- Es xifren les dades (grans) amb aquesta clau fent servir AES-256-GCM: ràpid i sense límit de mida.
- Es xifra només la clau de sessió (32 bytes) amb la criptografia asimètrica, fent servir la clau pública del destinatari. Aquí la lentitud i el límit de mida de l'asimètrica no molesten: 32 bytes caben de sobres i es xifren una sola vegada.
- S'envia el sobre: la clau de sessió xifrada + les dades xifrades, juntes.
El destinatari fa el camí invers: amb la seva clau privada obre el sobre i recupera la clau de sessió; amb ella desxifra les dades.
flowchart TD
subgraph Emissor["MediNube (emissor)"]
K["Clau de sessio AES-256 ALEATORIA"] --> C1["AES-256-GCM xifra l'EXPORT (gran, rapid)"]
PUB["Publica de la Clinica Sol"] --> C2["Xifra NOMES la clau de sessio (32 bytes)"]
K --> C2
end
C2 --> S["SOBRE: clau xifrada + dades xifrades"]
C1 --> S
S -->|canal public| Dest
subgraph Dest["Clinica Sol (destinatari)"]
PRIV["La seva PRIVADA"] --> D1["Obre el sobre -> recupera la clau de sessio"]
D1 --> D2["AES-256-GCM desxifra l'export"]
end
La bellesa del patró: obtens la confidencialitat davant d'un destinatari amb clau pública (ningú més pot obrir el sobre) amb la velocitat de la simètrica (les dades van amb AES). I com que la clau de sessió és fresca a cada enviament, dos exports idèntics produeixen sobres completament diferents. Només falta decidir com es tanca el sobre —el pas 3— i aquí hi ha dues escoles.
Sobre digital amb RSA-OAEP (variant clàssica)
Si el destinatari té una clau RSA (com l'hospital públic de la integració de MediNube), tancar el sobre és literalment el que vas aprendre a 04-01: xifrar els 32 bytes de la clau de sessió amb RSA-OAEP.
import os
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
OAEP = padding.OAEP(mgf=padding.MGF1(hashes.SHA256()),
algorithm=hashes.SHA256(), label=None)
def xifrar_hibrid_rsa(dades: bytes, aad: bytes, pub_rsa) -> dict:
"""Sobre digital: AES-GCM per a les dades, RSA-OAEP per a la clau."""
clau_sessio = AESGCM.generate_key(bit_length=256) # aleatoria, d'un sol us
nonce = os.urandom(12) # nonce fresc (modul 2)
dades_xifrades = AESGCM(clau_sessio).encrypt(nonce, dades, aad)
clau_xifrada = pub_rsa.encrypt(clau_sessio, OAEP) # nomes 32 bytes per RSA
return {"esquema": "rsa-oaep", "clau_xifrada": clau_xifrada,
"nonce": nonce, "dades_xifrades": dades_xifrades}
def desxifrar_hibrid_rsa(sobre: dict, aad: bytes, priv_rsa) -> bytes:
clau_sessio = priv_rsa.decrypt(sobre["clau_xifrada"], OAEP) # obre el sobre
return AESGCM(clau_sessio).decrypt(sobre["nonce"], sobre["dades_xifrades"], aad)
# --- Prova amb un export "gran" ---
priv = rsa.generate_private_key(public_exponent=65537, key_size=3072)
export = b"HISTORIALS CLINICA SOL\n" + b"ana.perez;A+;penicilina\n" * 5000
aad = b"destino=clinica-sol;formato=v1"
sobre = xifrar_hibrid_rsa(export, aad, priv.public_key())
print(len(export), "bytes de dades ->", len(sobre["clau_xifrada"]), "bytes de clau xifrada")
assert desxifrar_hibrid_rsa(sobre, aad, priv) == exportFixa't en el punt clau: export pot pesar megabytes i a l'RSA no li importa, perquè l'RSA només toca els 32 bytes de la clau de sessió. El límit de 318 bytes de 04-01 deixa de ser un problema. Això és "l'RSA xifra claus, no dades" fet codi.
Sobre digital amb X25519 efímer (variant moderna, ECIES-like)
I si el destinatari té una clau de corba el·líptica (X25519), que —com vas veure a 04-04— no xifra directament? Es combina l'intercanvi de claus de la lliçó anterior amb el sobre: l'emissor genera un parell X25519 efímer, fa ECDH contra la pública estàtica del destinatari, deriva la clau de sessió amb HKDF, i adjunta la seva pública efímera al sobre. El destinatari reconstrueix el mateix secret amb la seva privada i la pública efímera rebuda. Aquest patró té nom propi: ECIES (Elliptic Curve Integrated Encryption Scheme), i hereta gratis la forward secrecy de 04-04.
from cryptography.hazmat.primitives.asymmetric import x25519
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
def _derivar(secret: bytes) -> bytes:
return HKDF(algorithm=hashes.SHA256(), length=32, salt=None,
info=b"medinube:sobre-hibrido:v1").derive(secret)
def xifrar_hibrid_x25519(dades: bytes, aad: bytes, pub_dest: x25519.X25519PublicKey) -> dict:
efimera = x25519.X25519PrivateKey.generate() # parell EFIMER d'un sol us
clau_sessio = _derivar(efimera.exchange(pub_dest)) # ECDH + HKDF (04-04)
nonce = os.urandom(12)
dades_xifrades = AESGCM(clau_sessio).encrypt(nonce, dades, aad)
return {"esquema": "x25519-ecies",
"pub_efimera": efimera.public_key().public_bytes_raw(), # viatja EN CLAR
"nonce": nonce, "dades_xifrades": dades_xifrades}
def desxifrar_hibrid_x25519(sobre: dict, aad: bytes, priv_dest: x25519.X25519PrivateKey) -> bytes:
pub_efimera = x25519.X25519PublicKey.from_public_bytes(sobre["pub_efimera"])
clau_sessio = _derivar(priv_dest.exchange(pub_efimera)) # mateix secret
return AESGCM(clau_sessio).decrypt(sobre["nonce"], sobre["dades_xifrades"], aad)
# --- Prova ---
priv_clinica = x25519.X25519PrivateKey.generate()
export = b"HISTORIALS\n" + b"ana.perez;A+\n" * 5000
aad = b"destino=clinica-sol;formato=v1"
sobre = xifrar_hibrid_x25519(export, aad, priv_clinica.public_key())
assert desxifrar_hibrid_x25519(sobre, aad, priv_clinica) == exportDiferències davant de la variant RSA, que convé tenir clares:
| RSA-OAEP (clàssic) | X25519 efímer / ECIES (modern) | |
|---|---|---|
| Què viatja per "obrir el sobre" | La clau de sessió xifrada amb la pública | La pública efímera de l'emissor (en clar) |
| Com obté la clau el destinatari | La desxifra amb la seva privada | La re-deriva per ECDH + HKDF |
| Forward secrecy | No (si roben la privada RSA, s'obren tots els sobres passats) | Sí (parell efímer per sobre, es descarta) |
| Mida extra del sobre | 384 bytes (clau xifrada, RSA-3072) | 32 bytes (pública efímera) |
| Quan fer-la servir | Destinatari amb clau RSA / requisit heretat | Elecció moderna per defecte |
A l'ECIES no es "xifra" la clau de sessió: es deriva d'un intercanvi, i l'única cosa que viatja és la pública efímera —inofensiva, com tota pública. Per això dona forward secrecy gairebé gratis, la mateixa propietat que feia valuoses les claus efímeres a 04-04.
El format de sobre versionat de MediNube
Els diccionaris d'exemple estan bé per aprendre, però per enviar el sobre per un canal o desar-lo a disc cal un format de bytes concret i versionat, coherent amb l'estil v1 que arrosseguem des del mòdul 2 (versio || ...). Així, medinube/sobre.py pot evolucionar sense trencar sobres antics (criptoagilitat, regla d'or 8):
# medinube/sobre.py -- format de sobre hibrid versionat
#
# byte 0 : versio de format
# 0x01 -> RSA-OAEP : 0x01 || clau_xifrada(384) || nonce(12) || dades_xifrades
# 0x02 -> X25519 : 0x02 || pub_efimera(32) || nonce(12) || dades_xifrades
#
# Les dades van amb AES-256-GCM (tag inclos). L'AAD del curs identifica el desti.
def empaquetar_x25519(sobre: dict) -> bytes:
return b"\x02" + sobre["pub_efimera"] + sobre["nonce"] + sobre["dades_xifrades"]
def desempaquetar_x25519(blob: bytes, aad: bytes, priv_dest) -> bytes:
if blob[0] != 0x02:
raise ValueError(f"versio de sobre no suportada: {blob[0]:#x}")
sobre = {"pub_efimera": blob[1:33], "nonce": blob[33:45], "dades_xifrades": blob[45:]}
return desxifrar_hibrid_x25519(sobre, aad, priv_dest)
# L'export d'historials de ana.perez, llest per viatjar com un sol blob:
blob = empaquetar_x25519(xifrar_hibrid_x25519(export, aad, priv_clinica.public_key()))
print(blob[0], len(blob)) # 2 <longitud total>
assert desempaquetar_x25519(blob, aad, priv_clinica) == exportEl primer byte mana: qui rep el sobre llegeix la versió i sap exactament com interpretar-lo. El dia que MediNube afegeixi un esquema post-quàntic (06-05), serà 0x03; els sobres 0x01 i 0x02 es continuaran desxifrant sense tocar res. És la mateixa disciplina del format v1 d'AES-GCM (02-03) i de la signatura v1=<hex> de les receptes (04-03): la versió, sempre al davant.
Recordatori de compliance. Un export d'historials és dada sanitària de categoria especial. Aquest codi mostra la mecànica criptogràfica amb dades fictícies; un desplegament real exigeix revisió de seguretat i de protecció de dades (RGPD) —igual que hem advertit amb les receptes.
On viu aquest patró al món real
El xifratge híbrid no és un truc d'aquest curs: és el patró dominant. Gairebé qualsevol cosa xifrada que facis servir diàriament és un sobre digital:
- TLS / HTTPS (mòdul 5, lliçó 05-02): cada visita a
https://portal.medinube.examplenegocia una clau de sessió simètrica mitjançant intercanvi asimètric (ECDHE) i xifra el trànsit amb AES-GCM o ChaCha20-Poly1305. És exactament el d'aquesta lliçó, amb autenticació per certificat. - PGP/GPG i age: xifratge de fitxers i correu. Generen una clau de sessió per missatge i la protegeixen amb la pública del destinatari —sobre digital de manual.
ageés la versió moderna i minimalista, construïda sobre X25519 com la teva variant ECIES. - KMS amb envelope encryption (avanç de 06-01): els serveis de gestió de claus al núvol xifren les teves dades amb una "clau de dades" (DEK) simètrica i, al seu torn, xifren aquesta DEK amb una "clau mestra" (KEK) que mai no surt del servei. El vocabulari canvia (DEK/KEK), el patró és idèntic: xifra les dades amb una clau simètrica, protegeix aquesta clau amb una altra criptografia. Ho tornaràs a veure al mòdul 6.
- Missatgeria E2E (Signal, WhatsApp): fan servir variants molt sofisticades (el protocol de doble ratchet) que van més enllà del sobre simple, però el seu fonament és el mateix —X25519 per acordar claus, simètrica per als missatges. Només ho mencionem; el seu disseny dona per a un curs propi.
Que reconeguis el patró sota tots aquests noms és una de les metes del curs: no són deu tecnologies diferents, és una idea aplicada deu vegades.
Mapa de decisió del mòdul
Tanca els ulls i repassa el mòdul sencer: quina primitiva asimètrica fer servir segons el que necessitis? Aquesta taula és el resum operatiu de les cinc lliçons:
| Necessito... | Objectiu (mòdul 1) | Primitiva | Lliçó |
|---|---|---|---|
| Xifrar cap a algú una dada petita (una clau) | Confidencialitat | RSA-OAEP | 04-01 |
| Xifrar cap a algú dades grans | Confidencialitat | Híbrid: AES-GCM + (RSA-OAEP o X25519/ECIES) | 04-05 |
| Signar alguna cosa verificable per qualsevol, amb no repudi | Autenticitat + no repudi | Ed25519 (o RSA-PSS) | 04-03 |
| Acordar una clau per un canal públic | (habilita confidencialitat) | ECDH amb X25519 (+ HKDF) | 04-04 |
| Claus petites i ràpides per a tot l'anterior | — | Corba el·líptica (Ed25519 / X25519) | 04-02 |
Regla de butxaca per triar de memòria: xifrar-cap-a-algú → híbrid (sobre); demostrar qui ho ha emès → signatura (Ed25519); posar-se d'acord en una clau → intercanvi (X25519). Tres verbs, tres eines. I totes, per sota, recolzant-se en la simètrica i els hashes dels mòduls 2 i 3: res s'ha llençat, tot s'ha combinat.
Errors Comuns i Consells
- Trossejar dades grans amb RSA en lloc de fer servir l'híbrid. L'antipatró que ja avisàvem a 04-01: xifrar un fitxer en blocs de 318 bytes amb RSA és lentíssim i criptogràficament fràgil. El sobre digital és la resposta —una clau de sessió, un xifratge AES.
- Reutilitzar la clau de sessió entre missatges. Ha de ser aleatòria i d'un sol ús per sobre. Reutilitzar-la reintrodueix el determinisme (dos exports iguals donarien xifrats iguals) i arrisca la regla del nonce d'AES-GCM.
- Oblidar l'AAD del sobre. Lliga el xifratge al seu context (destí, format) igual que a 02-03. Sense AAD, un atacant podria reencaminar un sobre vàlid a un altre destinatari o format sense que el desxifratge ho detecti.
- Desar el sobre sense versió. El byte de versió al davant no és decoratiu: és el que permet afegir ECIES o post-quàntica demà sense trencar els sobres d'avui. Format sense versionar = migració impossible.
- Creure que el sobre autentica l'emissor. El xifratge híbrid dona confidencialitat, no autenticitat: qualsevol amb la teva pública et pot fabricar un sobre. Si necessites saber qui l'ha enviat, cal signar a més de xifrar (04-03) —signar-i-després-xifrar, o esquemes combinats; no ho dona el sobre sol.
- Fer servir la variant RSA i esperar forward secrecy. L'RSA-OAEP no la té: si algun dia roben la privada RSA, tots els sobres passats gravats s'obren. Si la forward secrecy importa, X25519 efímer.
- I el de sempre: donar per bona la clau pública del destinatari. Tot l'híbrid pressuposa que la pública de la Clínica Sol és de veritat de la Clínica Sol. Encara no ho sabem —és el deute que tanca el mòdul, just a sota.
Exercicis
-
Sobre versionat d'anada i tornada. Fent servir
xifrar_hibrid_x25519i el format demedinube/sobre.py: xifra l'exportb"HISTORIAL ana.perez\n" * 1000amb AADb"destino=clinica-sol;formato=v1", empaqueta'l en unblobde bytes, imprimeix el seu primer byte i la seva longitud, i desempaqueta'l comprovant que recuperes l'original. Després, canvia un byte de les dades xifrades del blob i comprova quina excepció salta en desempaquetar i per què (pista: AES-GCM, mòdul 2). -
Els dos sobres, comparats. Xifra el mateix export amb la variant RSA-OAEP i amb la X25519. (a) Compara la mida del material que viatja per "obrir el sobre" en cada cas. (b) Xifra dues vegades el mateix export amb la variant X25519 i comprova que les públiques efímeres (i per tant els sobres) són diferents. (c) Explica quina propietat de seguretat dona aquesta diferència i quina de les dues variants la té.
-
Confidencialitat no és autenticitat. MediNube rep un sobre híbrid X25519 que desxifra sense error i conté una ordre: "donar d'alta un metge nou". Raona (sense codi): (a) demostra el desxifratge correcte que el sobre el va enviar la Clínica Sol? (b) Si no, qui l'ha pogut fabricar? (c) Quina primitiva d'aquest mòdul caldria afegir perquè MediNube pogués confiar en l'emissor, i quin problema pendent reapareixeria en fer-ho?
Solucions
export = b"HISTORIAL ana.perez\n" * 1000
aad = b"destino=clinica-sol;formato=v1"
blob = empaquetar_x25519(xifrar_hibrid_x25519(export, aad, priv_clinica.public_key()))
print(blob[0], len(blob)) # 2 <longitud>
assert desempaquetar_x25519(blob, aad, priv_clinica) == export
trencat = blob[:-1] + bytes([blob[-1] ^ 1]) # alterem un byte del final (dades)
try:
desempaquetar_x25519(trencat, aad, priv_clinica)
except Exception as e:
print(type(e).__name__) # InvalidTagSalta InvalidTag: el tag d'autenticació d'AES-256-GCM (mòdul 2) detecta que un byte del xifrat ha canviat i es nega a desxifrar. El sobre híbrid hereta la integritat de l'AEAD interior: no només és confidencial, també detecta manipulació de les dades.
priv_rsa = rsa.generate_private_key(public_exponent=65537, key_size=3072) s_rsa = xifrar_hibrid_rsa(export, aad, priv_rsa.public_key()) s_ec1 = xifrar_hibrid_x25519(export, aad, priv_clinica.public_key()) s_ec2 = xifrar_hibrid_x25519(export, aad, priv_clinica.public_key()) print(len(s_rsa["clau_xifrada"])) # (a) 384 bytes print(len(s_ec1["pub_efimera"])) # (a) 32 bytes print(s_ec1["pub_efimera"] != s_ec2["pub_efimera"]) # (b) True: efimeres diferents
(a) L'RSA fa viatjar 384 bytes (la clau de sessió xifrada); l'X25519, només 32 (la pública efímera). (b) Cada xifratge X25519 genera un parell efímer nou, així que les públiques —i els sobres sencers— difereixen encara que l'export sigui idèntic. (c) Aquesta renovació per sobre dona forward secrecy: la té la variant X25519 efímera, no l'RSA-OAEP (la privada única de la qual, si es roba, obre tots els sobres passats).
- (a) No. El xifratge híbrid només prova que algú amb la clau pública de MediNube va fabricar el sobre —i aquesta clau és pública. Desxifrar bé demostra confidencialitat i integritat de les dades, no la identitat de l'emissor. (b) Qualsevol: la pública de MediNube està publicada, així que un atacant pot fabricar un sobre perfectament vàlid amb l'ordre que vulgui. (c) Caldria signar el contingut amb la clau privada de l'emissor (Ed25519, 04-03) i que MediNube verifiqui amb la pública de la Clínica: això afegeix autenticitat i no repudi. Però en fer-ho reapareix la pregunta del mòdul: com sap MediNube que aquesta clau pública de verificació és de veritat de la Clínica Sol? La resposta —certificats i autoritats de confiança— és el mòdul 5.
Conclusió
Has tancat el cercle. El xifratge híbrid combina el millor de les dues famílies del curs: una clau de sessió simètrica aleatòria xifra les dades amb AES-256-GCM (ràpid, sense límit de mida), i la criptografia asimètrica protegeix només aquesta clau (on la seva lentitud i el seu límit no importen). Ho vas muntar en les seves dues formes —RSA-OAEP clàssic i el patró modern X25519 efímer (ECIES), aquest últim amb forward secrecy de regal—, en un format de sobre versionat (0x01/0x02, la versió sempre al davant) coherent amb tot el curs, i vas reconèixer el mateix patró bategant sota el TLS, PGP/age, l'envelope encryption dels KMS i la missatgeria E2E. Amb això acaba el mòdul 4 i, amb ell, la teva caixa d'eines queda completa: tens la simètrica (mòdul 2) per xifrar ràpid, els hashes i MAC (mòdul 3) per a integritat i autenticitat amb secret compartit, i l'asimètrica (aquest mòdul) per signar, acordar claus i xifrar cap a desconeguts sense secret previ. Pots xifrar, signar, verificar i acordar claus per un canal públic. I tot i així, lliçó rere lliçó —04-01, 04-03, 04-04, i ara mateix a l'últim exercici— ha reaparegut sempre la mateixa esquerda: tota la matemàtica és impecable, però descansa sobre una suposició sense verificar, que la clau pública que tens és de qui creus. MalloryClinic continua allà fora, esperant que algú confiï en una clau sense comprovar-la. No ens falten primitives noves: ens falta confiança —una manera de saber, amb suport verificable, de qui és cada clau pública. Aquesta és l'única peça que queda, i és justament el que construeix el Mòdul 5: PKI, Certificats i TLS, començant pels certificats X.509 i les autoritats de certificació a la lliçó 05-01. Allà, per fi, tancarem l'esquerda que portem arrossegant tot el mòdul. Ens veiem al mòdul 5.
Curs de Criptografia Aplicada
Mòdul 1: Fonaments de la Criptografia
- Què és la criptografia i per a què serveix
- Codificació, ofuscació i xifratge
- Aleatorietat i entropia
- El principi de Kerckhoffs i les regles d'or
Mòdul 2: Criptografia Simètrica
- Xifratge simètric: AES i ChaCha20
- Modes d'operació
- Xifratge autenticat (AEAD)
- Derivació de claus (KDF)
Mòdul 3: Hashes, MAC i Contrasenyes
- Funcions hash criptogràfiques
- Autenticació de missatges amb HMAC
- Emmagatzematge segur de contrasenyes
Mòdul 4: Criptografia Asimètrica
- Fonaments de clau pública i RSA
- Criptografia de corba el·líptica
- Signatures digitals
- Intercanvi de claus: Diffie-Hellman
- Xifratge híbrid
Mòdul 5: PKI, Certificats i TLS
- Certificats X.509 i autoritats de certificació
- TLS a la pràctica
- Gestió del cicle de vida dels certificats
