Volver a Explorar
PatrónTécnicoCiberseguridadAvanzado

SD-JWT — cómo funciona la divulgación selectiva

8 minVerificado · 2026-05-18

Este artículo profundiza la mecánica interna de SD-JWT (Selective Disclosure JWT) para entender por qué un verificador puede confirmar atributos sin ver los datos completos. Es la pieza central del modelo de privacy en SD-JWT VC.

El problema que SD-JWT resuelve

Un JWT clásico firma toda la payload junta. Si querés ocultar parte:

  • No podés. Ocultarla rompe la firma.
  • Tenés que emitir múltiples JWTs (uno por subset de atributos).
  • O usar técnicas más complejas como ZKPs.

SD-JWT propone una solución diferente: en vez de firmar los datos directamente, firmar los hashes de cada atributo. Cuando hay que presentar, se revela solo lo necesario + los hashes correspondientes.

La estructura interna

Un SD-JWT consiste en:

<JWT>~<Disclosure 1>~<Disclosure 2>~<Disclosure 3>~...

Donde:

  • <JWT> es un JWT firmado normalmente.
  • <Disclosure N> son strings que pueden o no incluirse cuando se presenta.

Anatomía del JWT

El payload del JWT incluye los hashes:

{
  "iss": "did:web:salta.gob.ar",
  "sub": "did:key:z6Mk...",
  "iat": 1714521600,
  "_sd": [
    "K7H_5xV2bL...",
    "P8N_2bL3kM...",
    "Q3J_9wM7nP..."
  ],
  "_sd_alg": "sha-256",
  "vct": "ConstanciaDomicilio"
}
  • _sd es un array de hashes (uno por cada atributo selectively disclosable).
  • _sd_alg indica qué algoritmo de hash se usó.
  • Los atributos que NO están bajo _sd son visibles always (ej: iss, iat).

Anatomía de un Disclosure

Cada disclosure es una cadena base64url-encoded que contiene un JSON con tres elementos:

[
  "8I-vSb04...salt",
  "domicilio",
  "Av. Belgrano 1234, Salta"
]
  • El primer elemento es un salt (aleatorio, único por disclosure).
  • El segundo es el nombre del atributo.
  • El tercero es el valor del atributo.

Encoded a base64url, esto es un string como WyI4SS12U2IwNCIsImRvbWljaWxpbyIsIkF2LiBCZWxncmFubyAxMjM0Il0=.

La conexión entre JWT y Disclosure

Cada hash en _sd del JWT corresponde a un Disclosure. La conexión es matemática:

hash = sha-256(base64url(JSON.stringify([salt, name, value])))

El emisor:

  1. Genera salt aleatorio por cada atributo.
  2. Calcula el hash de la concatenación.
  3. Incluye el hash en _sd y firma todo.
  4. Manda al holder el JWT + los Disclosures.

El holder presenta selectivamente

Cuando el holder quiere presentar solo "nombre" y "domicilio":

  1. 1
    Toma el JWT completo (no se modifica).
  2. 2
    Toma solo los Disclosures de los atributos que quiere revelar.
  3. 3
    Manda al verifier: <JWT>~<Disclosure_nombre>~<Disclosure_domicilio>~
  4. 4
    Los Disclosures de otros atributos (fecha_nacimiento, etc.) NO se incluyen.

El verifier verifica

El verifier recibe <JWT>~<Disclosure_nombre>~<Disclosure_domicilio>~. Sus pasos:

  1. 1
    Validar firma del JWT. Confirma que viene del emisor genuino.
  2. 2
    Para cada Disclosure recibido:
    • Decode base64url para obtener [salt, name, value].
    • Calcular el hash sha-256 de base64url(JSON.stringify([salt, name, value])).
    • Verificar que ese hash está en el array _sd del JWT.
  3. 3
    Si todos los Disclosures matchean hashes: los atributos revelados son auténticos.
  4. 4
    Para los atributos no revelados: el verifier ve los hashes pero no puede inferir el valor (los hashes son irreversibles).

Por qué los salts importan

Sin salt, el ataque sería:

  • Si el verifier sabe que un atributo está bajo _sd y conoce el valor probable ("Juan", "María", etc.), puede calcular el hash de cada candidato y comparar.
  • Salt previene este ataque porque el atacante necesitaría adivinar el salt + el valor.

Cada salt debe ser:

  • Aleatorio (no derivable).
  • Único por disclosure (no compartir entre atributos).
  • Suficientemente largo (típicamente 128 bits / 16 bytes).

Selective disclosure de elementos en arrays

SD-JWT también soporta divulgación selectiva de elementos individuales dentro de un array:

{
  "address": {
    "street": "Av. Belgrano 1234",
    "city": "Salta",
    "country": "Argentina"
  }
}

Con SD-JWT, cada campo puede ser selectively disclosable independientemente. El holder puede revelar country sin revelar street o city.

Key binding — la pieza adicional

Más allá de selective disclosure, SD-JWT VC incluye key binding para evitar que un atacante que intercepta el JWT lo presente como suyo.

El JWT incluye en cnf la clave pública del holder. Al presentar, el holder firma un JWT adicional (key binding JWT, KB-JWT) sobre el nonce + audience. El verifier verifica esa firma con la cnf clave del SD-JWT original.

<SD-JWT>~<Disclosure1>~<Disclosure2>~<KB-JWT>

Sin KB-JWT, alguien que intercepta el SD-JWT puede presentarlo a otro verifier. Con KB-JWT, cada presentación requiere firma fresca del holder.

Implementación

Librerías que soportan SD-JWT VC:

LenguajeLibrería
JavaScript/TypeScript@sd-jwt/core, @sd-jwt/vc
Pythonsd-jwt-python
Gosd-jwt-go
Javasd-jwt-java
Rustsd-jwt-rs

Implementar SD-JWT desde cero es complejo. Usar librerías validadas.

Referencias

Relacionados

Tagssd-jwtmecanicaselective-disclosurehashes