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¶
- Selective Disclosure - Learn to hide and reveal specific claims
- Holder Binding - Add cryptographic proof of ownership
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 |