Skip to content

Tutorial: Hello SD-JWT

Create your first Selective Disclosure JWT in 5 minutes.

Time: 5 minutes
Level: Beginner
Sample: samples/SdJwt.Net.Samples/01-Beginner/01-HelloSdJwt.cs

What you will learn

  • How to create an SD-JWT issuer
  • How to sign claims with selective disclosure
  • How to parse and inspect an SD-JWT structure

Simple explanation

This tutorial creates a token where some facts (claims) can be hidden or revealed later. You will see how a set of claims becomes an SD-JWT that separates signed data from individually disclosable values.

Packages used

Package Purpose
SdJwt.Net Core SD-JWT issuance and parsing

Where this fits

flowchart LR
    A["Create Keys"] --> B["Issue SD-JWT"]
    B --> C["Hold / Store"]
    C --> D["Present"]
    D --> E["Verify"]
    style A fill:#2a6478,color:#fff
    style B fill:#2a6478,color:#fff

Prerequisites

  • .NET 9.0 SDK installed
  • Basic understanding of JWTs

Step 1: Create keys

Every SD-JWT system needs cryptographic keys. The issuer signs with a private key, and verifiers check with the public key.

using System.Security.Cryptography;
using Microsoft.IdentityModel.Tokens;

// Create an ECDSA key pair (P-256 curve)
using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
var issuerKey = new ECDsaSecurityKey(ecdsa) { KeyId = "my-key-1" };

Step 2: Create the issuer

The SdIssuer class handles SD-JWT creation:

using SdJwt.Net.Issuer;

var issuer = new SdIssuer(issuerKey, SecurityAlgorithms.EcdsaSha256);

Step 3: Define claims

Create a JWT payload with the claims you want to include:

using System.IdentityModel.Tokens.Jwt;

var claims = new JwtPayload
{
    ["iss"] = "https://issuer.example.com",
    ["sub"] = "user123",
    ["iat"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
    ["given_name"] = "Alice",
    ["family_name"] = "Smith",
    ["email"] = "alice@example.com"
};

Step 4: Configure selective disclosure

Specify which claims can be selectively disclosed:

using SdJwt.Net.Models;

var options = new SdIssuanceOptions
{
    DisclosureStructure = new
    {
        given_name = true,   // Can be hidden/revealed
        family_name = true,  // Can be hidden/revealed
        email = true         // Can be hidden/revealed
    }
};

Step 5: Issue the SD-JWT

var result = issuer.Issue(claims, options);

Console.WriteLine($"SD-JWT created with {result.Disclosures.Count} disclosures");
Console.WriteLine($"Issuance: {result.Issuance}");

Understanding the output

The result.Issuance string contains:

  • The signed JWT (with digests instead of actual claim values)
  • Disclosure strings (Base64URL encoded claim data)
  • Separated by ~ characters

Format: <JWT>~<disclosure1>~<disclosure2>~...~

Complete example

using System.Security.Cryptography;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using SdJwt.Net.Issuer;
using SdJwt.Net.Models;

// Setup
using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
var issuerKey = new ECDsaSecurityKey(ecdsa) { KeyId = "key-1" };
var issuer = new SdIssuer(issuerKey, SecurityAlgorithms.EcdsaSha256);

// Claims
var claims = new JwtPayload
{
    ["iss"] = "https://issuer.example.com",
    ["sub"] = "user123",
    ["given_name"] = "Alice",
    ["family_name"] = "Smith"
};

// Options
var options = new SdIssuanceOptions
{
    DisclosureStructure = new { given_name = true, family_name = true }
};

// Issue
var result = issuer.Issue(claims, options);
Console.WriteLine($"Created SD-JWT with {result.Disclosures.Count} disclosures");

Run the sample

cd samples/SdJwt.Net.Samples
dotnet run -- 1.1

Expected output

SD-JWT created with 3 disclosures
Issuance: eyJhbGciOi...~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkFsaWNlIl0~...~

Demo vs production

This tutorial uses in-memory keys for simplicity. In production, use a key management service (Azure Key Vault, AWS KMS, or HSM) and persist keys securely.

Common mistakes

  • Forgetting to add claims to the disclosure structure (they stay visible in the JWT payload)
  • Using RSA when HAIP requires ECDSA P-256

Next steps

Key concepts

Term Description
SD-JWT Selective Disclosure JSON Web Token
Disclosure Base64URL-encoded claim that can be revealed
Digest Hash of a disclosure stored in the JWT
Issuance Complete SD-JWT string with all disclosures