or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

compression.mdindex.mdjwk-support.mdjwt-building.mdjwt-parsing.mdsecurity-algorithms.mdutilities.md

jwk-support.mddocs/

0

# JWK Support

1

2

The JWK (JSON Web Key) Support in JJWT Implementation provides comprehensive RFC 7517-compliant functionality for creating, parsing, managing, and using JSON Web Keys and JWK Sets. This includes support for all standard key types, operations, and X.509 certificate integration.

3

4

## Core JWK Classes

5

6

### JWK Building and Creation

7

8

```java { .api }

9

import io.jsonwebtoken.Jwts;

10

import io.jsonwebtoken.security.Jwk;

11

import io.jsonwebtoken.security.JwkSet;

12

import io.jsonwebtoken.security.JwkBuilder;

13

import io.jsonwebtoken.security.KeyOperation;

14

import javax.crypto.SecretKey;

15

import java.security.KeyPair;

16

import java.security.PublicKey;

17

import java.security.PrivateKey;

18

import java.security.cert.X509Certificate;

19

import java.util.List;

20

import java.util.Set;

21

22

// Create JWK from existing key

23

SecretKey secretKey = Jwts.SIG.HS256.key().build();

24

Jwk<SecretKey> secretJwk = Jwts.JWK.builder(secretKey).build();

25

26

// Create JWK from key pair

27

KeyPair rsaKeyPair = Jwts.SIG.RS256.keyPair().build();

28

Jwk<PublicKey> publicJwk = Jwts.JWK.builder(rsaKeyPair.getPublic()).build();

29

Jwk<PrivateKey> privateJwk = Jwts.JWK.builder(rsaKeyPair.getPrivate()).build();

30

31

// Create JWK pair from KeyPair

32

Jwk<KeyPair> pairJwk = Jwts.JWK.builder(rsaKeyPair).build();

33

```

34

35

### JWK with Metadata

36

37

```java { .api }

38

// JWK with comprehensive metadata

39

Jwk<SecretKey> metadataJwk = Jwts.JWK.builder(secretKey)

40

.id("hmac-key-1")

41

.algorithm("HS256")

42

.operations()

43

.add(KeyOperation.SIGN)

44

.add(KeyOperation.VERIFY)

45

.and()

46

.build();

47

48

// RSA JWK with rich metadata

49

Jwk<PublicKey> rsaJwk = Jwts.JWK.builder(rsaKeyPair.getPublic())

50

.id("rsa-pub-2024-01")

51

.algorithm("RS256")

52

.keyUse("sig") // signature use

53

.operations()

54

.add(KeyOperation.VERIFY)

55

.and()

56

.build();

57

58

// EC JWK with curve information

59

KeyPair ecKeyPair = Jwts.SIG.ES256.keyPair().build();

60

Jwk<PublicKey> ecJwk = Jwts.JWK.builder(ecKeyPair.getPublic())

61

.id("ec-p256-key")

62

.algorithm("ES256")

63

.keyUse("sig")

64

.build();

65

```

66

67

## Key Type Support

68

69

### Symmetric Keys (oct)

70

71

```java { .api }

72

// HMAC secret keys

73

SecretKey hs256Key = Jwts.SIG.HS256.key().build();

74

SecretKey hs384Key = Jwts.SIG.HS384.key().build();

75

SecretKey hs512Key = Jwts.SIG.HS512.key().build();

76

77

Jwk<SecretKey> octJwk256 = Jwts.JWK.builder(hs256Key)

78

.id("hmac-256-key")

79

.algorithm("HS256")

80

.operations()

81

.add(KeyOperation.SIGN)

82

.add(KeyOperation.VERIFY)

83

.and()

84

.build();

85

86

// AES encryption keys

87

SecretKey aes128Key = Jwts.ENC.A128GCM.key().build();

88

SecretKey aes256Key = Jwts.ENC.A256GCM.key().build();

89

90

Jwk<SecretKey> aesJwk = Jwts.JWK.builder(aes256Key)

91

.id("aes-256-gcm-key")

92

.algorithm("A256GCM")

93

.operations()

94

.add(KeyOperation.ENCRYPT)

95

.add(KeyOperation.DECRYPT)

96

.and()

97

.build();

98

99

// Key wrapping keys

100

SecretKey kekKey = Jwts.KEY.A256KW.key().build();

101

Jwk<SecretKey> kekJwk = Jwts.JWK.builder(kekKey)

102

.id("kek-256")

103

.algorithm("A256KW")

104

.operations()

105

.add(KeyOperation.WRAP_KEY)

106

.add(KeyOperation.UNWRAP_KEY)

107

.and()

108

.build();

109

```

110

111

### RSA Keys

112

113

```java { .api }

114

// RSA signature keys

115

KeyPair rsaKeyPair = Jwts.SIG.RS256.keyPair()

116

.keySize(2048)

117

.build();

118

119

Jwk<PublicKey> rsaPublicJwk = Jwts.JWK.builder(rsaKeyPair.getPublic())

120

.id("rsa-2048-pub")

121

.algorithm("RS256")

122

.keyUse("sig")

123

.operations()

124

.add(KeyOperation.VERIFY)

125

.and()

126

.build();

127

128

Jwk<PrivateKey> rsaPrivateJwk = Jwts.JWK.builder(rsaKeyPair.getPrivate())

129

.id("rsa-2048-priv")

130

.algorithm("RS256")

131

.keyUse("sig")

132

.operations()

133

.add(KeyOperation.SIGN)

134

.and()

135

.build();

136

137

// RSA encryption keys

138

KeyPair rsaEncPair = Jwts.KEY.RSA_OAEP.keyPair()

139

.keySize(2048)

140

.build();

141

142

Jwk<PublicKey> rsaEncJwk = Jwts.JWK.builder(rsaEncPair.getPublic())

143

.id("rsa-enc-2048")

144

.algorithm("RSA-OAEP")

145

.keyUse("enc")

146

.operations()

147

.add(KeyOperation.ENCRYPT)

148

.and()

149

.build();

150

151

// RSA key pair JWK

152

Jwk<KeyPair> rsaPairJwk = Jwts.JWK.builder(rsaKeyPair)

153

.id("rsa-pair-2048")

154

.algorithm("RS256")

155

.keyUse("sig")

156

.build();

157

```

158

159

### Elliptic Curve Keys

160

161

```java { .api }

162

// P-256 curve (ES256)

163

KeyPair ecP256Pair = Jwts.SIG.ES256.keyPair().build();

164

165

Jwk<PublicKey> ecP256Jwk = Jwts.JWK.builder(ecP256Pair.getPublic())

166

.id("ec-p256-pub")

167

.algorithm("ES256")

168

.keyUse("sig")

169

.operations()

170

.add(KeyOperation.VERIFY)

171

.and()

172

.build();

173

174

// P-384 curve (ES384)

175

KeyPair ecP384Pair = Jwts.SIG.ES384.keyPair().build();

176

Jwk<PublicKey> ecP384Jwk = Jwts.JWK.builder(ecP384Pair.getPublic())

177

.id("ec-p384-pub")

178

.algorithm("ES384")

179

.build();

180

181

// P-521 curve (ES512)

182

KeyPair ecP521Pair = Jwts.SIG.ES512.keyPair().build();

183

Jwk<KeyPair> ecP521PairJwk = Jwts.JWK.builder(ecP521Pair)

184

.id("ec-p521-pair")

185

.algorithm("ES512")

186

.build();

187

188

// ECDH keys for encryption

189

KeyPair ecdhPair = Jwts.KEY.ECDH_ES.keyPair().build();

190

Jwk<PublicKey> ecdhJwk = Jwts.JWK.builder(ecdhPair.getPublic())

191

.id("ecdh-p256")

192

.algorithm("ECDH-ES")

193

.keyUse("enc")

194

.operations()

195

.add(KeyOperation.DERIVE_KEY)

196

.and()

197

.build();

198

```

199

200

### OKP Keys (EdDSA)

201

202

```java { .api }

203

// Ed25519 keys

204

KeyPair ed25519Pair = Jwts.SIG.EdDSA.keyPair().build();

205

206

Jwk<PublicKey> ed25519Jwk = Jwts.JWK.builder(ed25519Pair.getPublic())

207

.id("ed25519-key")

208

.algorithm("EdDSA")

209

.keyUse("sig")

210

.operations()

211

.add(KeyOperation.VERIFY)

212

.and()

213

.build();

214

215

Jwk<PrivateKey> ed25519PrivJwk = Jwts.JWK.builder(ed25519Pair.getPrivate())

216

.id("ed25519-priv")

217

.algorithm("EdDSA")

218

.keyUse("sig")

219

.operations()

220

.add(KeyOperation.SIGN)

221

.and()

222

.build();

223

224

// Ed448 support (if available)

225

try {

226

KeyPair ed448Pair = Jwts.SIG.EdDSA.keyPair()

227

.algorithm("Ed448")

228

.build();

229

230

Jwk<PublicKey> ed448Jwk = Jwts.JWK.builder(ed448Pair.getPublic())

231

.id("ed448-key")

232

.algorithm("EdDSA")

233

.build();

234

} catch (Exception e) {

235

// Ed448 might not be available in all JDK versions

236

}

237

```

238

239

## JWK Operations and Key Use

240

241

### Key Operations

242

243

```java { .api }

244

import io.jsonwebtoken.security.KeyOperationBuilder;

245

246

// Comprehensive key operations

247

Jwk<KeyPair> multiOpJwk = Jwts.JWK.builder(rsaKeyPair)

248

.id("multi-purpose-rsa")

249

.operations()

250

.add(KeyOperation.SIGN)

251

.add(KeyOperation.VERIFY)

252

.add(KeyOperation.ENCRYPT)

253

.add(KeyOperation.DECRYPT)

254

.add(KeyOperation.WRAP_KEY)

255

.add(KeyOperation.UNWRAP_KEY)

256

.and()

257

.build();

258

259

// Signature-only operations

260

Jwk<PublicKey> signOnlyJwk = Jwts.JWK.builder(publicKey)

261

.id("sign-only-key")

262

.keyUse("sig")

263

.operations()

264

.add(KeyOperation.VERIFY)

265

.and()

266

.build();

267

268

// Encryption-only operations

269

Jwk<PublicKey> encOnlyJwk = Jwts.JWK.builder(encryptionPublicKey)

270

.id("enc-only-key")

271

.keyUse("enc")

272

.operations()

273

.add(KeyOperation.ENCRYPT)

274

.add(KeyOperation.WRAP_KEY)

275

.and()

276

.build();

277

```

278

279

### Key Operation Policies

280

281

```java { .api }

282

import io.jsonwebtoken.impl.security.DefaultKeyOperationPolicyBuilder;

283

import io.jsonwebtoken.security.KeyOperationPolicy;

284

285

// Create operation policy

286

KeyOperationPolicy policy = Jwts.JWK.operationPolicy()

287

.add(KeyOperation.SIGN)

288

.add(KeyOperation.VERIFY)

289

.build();

290

291

// Apply policy during JWK creation

292

Jwk<KeyPair> policyJwk = Jwts.JWK.builder(keyPair)

293

.id("policy-controlled-key")

294

.operations()

295

.policy(policy)

296

.and()

297

.build();

298

```

299

300

## X.509 Certificate Integration

301

302

### JWK with Certificate Chain

303

304

```java { .api }

305

import java.security.cert.X509Certificate;

306

307

// Create JWK with X.509 certificate chain

308

X509Certificate[] certChain = loadCertificateChain();

309

PrivateKey certPrivateKey = loadPrivateKeyForCert();

310

311

Jwk<PrivateKey> certJwk = Jwts.JWK.builder(certPrivateKey)

312

.id("cert-key-2024")

313

.algorithm("RS256")

314

.x509CertChain(Arrays.asList(certChain))

315

.x509CertSha1Thumbprint(calculateSha1Thumbprint(certChain[0]))

316

.x509CertSha256Thumbprint(calculateSha256Thumbprint(certChain[0]))

317

.build();

318

319

// JWK with certificate URL

320

Jwk<PublicKey> certUrlJwk = Jwts.JWK.builder(publicKey)

321

.id("cert-url-key")

322

.x509Url(URI.create("https://certs.example.com/cert.pem"))

323

.build();

324

```

325

326

### Certificate Thumbprints

327

328

```java { .api }

329

import java.security.MessageDigest;

330

import java.security.NoSuchAlgorithmException;

331

332

// Calculate certificate thumbprints

333

public static String calculateSha1Thumbprint(X509Certificate cert) {

334

try {

335

MessageDigest md = MessageDigest.getInstance("SHA-1");

336

byte[] der = cert.getEncoded();

337

byte[] digest = md.digest(der);

338

return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);

339

} catch (Exception e) {

340

throw new RuntimeException("Failed to calculate thumbprint", e);

341

}

342

}

343

344

public static String calculateSha256Thumbprint(X509Certificate cert) {

345

try {

346

MessageDigest md = MessageDigest.getInstance("SHA-256");

347

byte[] der = cert.getEncoded();

348

byte[] digest = md.digest(der);

349

return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);

350

} catch (Exception e) {

351

throw new RuntimeException("Failed to calculate thumbprint", e);

352

}

353

}

354

355

// Use thumbprints in JWK

356

Jwk<PublicKey> thumbprintJwk = Jwts.JWK.builder(certPublicKey)

357

.id("thumbprint-key")

358

.x509CertSha1Thumbprint(calculateSha1Thumbprint(certificate))

359

.x509CertSha256Thumbprint(calculateSha256Thumbprint(certificate))

360

.build();

361

```

362

363

## JWK Sets

364

365

### Creating JWK Sets

366

367

```java { .api }

368

import io.jsonwebtoken.impl.security.DefaultJwkSetBuilder;

369

370

// Create JWK Set with multiple keys

371

Jwk<SecretKey> hmacJwk = Jwts.JWK.builder(hmacKey).id("hmac-1").build();

372

Jwk<PublicKey> rsaJwk = Jwts.JWK.builder(rsaPublicKey).id("rsa-1").build();

373

Jwk<PublicKey> ecJwk = Jwts.JWK.builder(ecPublicKey).id("ec-1").build();

374

375

JwkSet jwkSet = Jwts.JWK.set()

376

.add(hmacJwk)

377

.add(rsaJwk)

378

.add(ecJwk)

379

.build();

380

381

// Create JWK Set from collection

382

List<Jwk<?>> jwkList = Arrays.asList(hmacJwk, rsaJwk, ecJwk);

383

JwkSet collectionSet = Jwts.JWK.set()

384

.add(jwkList)

385

.build();

386

387

// Empty JWK Set

388

JwkSet emptySet = Jwts.JWK.set().build();

389

```

390

391

### JWK Set Operations

392

393

```java { .api }

394

// Access JWKs in set

395

Set<Jwk<?>> allJwks = jwkSet.getKeys();

396

int keyCount = jwkSet.size();

397

398

// Find JWK by ID

399

Jwk<?> foundJwk = jwkSet.getKeys().stream()

400

.filter(jwk -> "hmac-1".equals(jwk.getId()))

401

.findFirst()

402

.orElse(null);

403

404

// Filter by key type

405

List<Jwk<SecretKey>> secretKeys = jwkSet.getKeys().stream()

406

.filter(jwk -> jwk.toKey() instanceof SecretKey)

407

.map(jwk -> (Jwk<SecretKey>) jwk)

408

.collect(Collectors.toList());

409

410

// Filter by algorithm

411

List<Jwk<?>> rsaKeys = jwkSet.getKeys().stream()

412

.filter(jwk -> jwk.getAlgorithm() != null &&

413

jwk.getAlgorithm().startsWith("RS"))

414

.collect(Collectors.toList());

415

```

416

417

## JSON Serialization

418

419

### JWK to JSON

420

421

```java { .api }

422

// Serialize JWK to JSON

423

Jwk<SecretKey> secretJwk = Jwts.JWK.builder(secretKey)

424

.id("secret-key-1")

425

.algorithm("HS256")

426

.build();

427

428

String jwkJson = secretJwk.toJson();

429

430

// Pretty-printed JSON

431

String prettyJwkJson = secretJwk.toJson(true);

432

433

// JWK Set to JSON

434

String jwkSetJson = jwkSet.toJson();

435

String prettyJwkSetJson = jwkSet.toJson(true);

436

```

437

438

### JSON to JWK Parsing

439

440

```java { .api }

441

import io.jsonwebtoken.impl.security.DefaultJwkParserBuilder;

442

443

// Parse JWK from JSON

444

String jwkJson = """

445

{

446

"kty": "RSA",

447

"kid": "rsa-key-1",

448

"use": "sig",

449

"alg": "RS256",

450

"n": "...",

451

"e": "AQAB"

452

}

453

""";

454

455

Jwk<?> parsedJwk = Jwts.JWK.parser().build().parse(jwkJson);

456

457

// Type-safe parsing

458

if (parsedJwk.toKey() instanceof PublicKey) {

459

Jwk<PublicKey> publicJwk = (Jwk<PublicKey>) parsedJwk;

460

PublicKey publicKey = publicJwk.toKey();

461

}

462

463

// Parse JWK Set from JSON

464

String jwkSetJson = """

465

{

466

"keys": [

467

{

468

"kty": "RSA",

469

"kid": "rsa-1",

470

"use": "sig",

471

"alg": "RS256",

472

"n": "...",

473

"e": "AQAB"

474

},

475

{

476

"kty": "EC",

477

"kid": "ec-1",

478

"use": "sig",

479

"alg": "ES256",

480

"crv": "P-256",

481

"x": "...",

482

"y": "..."

483

}

484

]

485

}

486

""";

487

488

JwkSet parsedSet = Jwts.JWK.setParser().build().parse(jwkSetJson);

489

```

490

491

## JWK Parser Configuration

492

493

### Custom JWK Parser

494

495

```java { .api }

496

import io.jsonwebtoken.io.Deserializer;

497

import io.jsonwebtoken.security.JwkParser;

498

499

// Configure JWK parser with custom deserializer

500

Deserializer<Map<String, ?>> customDeserializer = createCustomDeserializer();

501

502

JwkParser customParser = Jwts.JWK.parser()

503

.json(customDeserializer)

504

.build();

505

506

// Provider-specific parsing

507

Provider customProvider = new BouncyCastleProvider();

508

509

JwkParser providerParser = Jwts.JWK.parser()

510

.provider(customProvider)

511

.build();

512

513

// Operation policy enforcement

514

KeyOperationPolicy restrictivePolicy = Jwts.JWK.operationPolicy()

515

.add(KeyOperation.VERIFY)

516

.build();

517

518

JwkParser restrictedParser = Jwts.JWK.parser()

519

.operationPolicy(restrictivePolicy)

520

.build();

521

```

522

523

### JWK Set Parser Configuration

524

525

```java { .api }

526

import io.jsonwebtoken.impl.security.DefaultJwkSetParserBuilder;

527

528

// Configure JWK Set parser

529

JwkSetParser setParser = Jwts.JWK.setParser()

530

.provider(customProvider)

531

.operationPolicy(policy)

532

.build();

533

534

// Parse with validation

535

JwkSet validatedSet = setParser.parse(jwkSetJson);

536

537

// Verify all keys meet policy requirements

538

boolean allKeysValid = validatedSet.getKeys().stream()

539

.allMatch(jwk -> validateKeyOperations(jwk));

540

```

541

542

## Dynamic JWK Building

543

544

### Dynamic Key Type Detection

545

546

```java { .api }

547

import io.jsonwebtoken.impl.security.DefaultDynamicJwkBuilder;

548

549

// Dynamic JWK building based on key type

550

Key unknownKey = getKeyFromSomewhere();

551

552

Jwk<?> dynamicJwk = Jwts.JWK.builder(unknownKey)

553

.id("dynamic-key")

554

.build();

555

556

// Type detection and handling

557

if (dynamicJwk.toKey() instanceof SecretKey) {

558

SecretKey secretKey = (SecretKey) dynamicJwk.toKey();

559

// Handle symmetric key

560

} else if (dynamicJwk.toKey() instanceof PublicKey) {

561

PublicKey publicKey = (PublicKey) dynamicJwk.toKey();

562

// Handle asymmetric public key

563

} else if (dynamicJwk.toKey() instanceof PrivateKey) {

564

PrivateKey privateKey = (PrivateKey) dynamicJwk.toKey();

565

// Handle asymmetric private key

566

}

567

```

568

569

## JWK Integration with JWT Operations

570

571

### Using JWK for JWT Creation

572

573

```java { .api }

574

// Create JWT using JWK

575

Jwk<PrivateKey> signingJwk = Jwts.JWK.builder(privateKey)

576

.id("signing-key-1")

577

.algorithm("RS256")

578

.build();

579

580

String jwt = Jwts.builder()

581

.subject("user")

582

.header()

583

.keyId(signingJwk.getId())

584

.and()

585

.signWith(signingJwk.toKey())

586

.compact();

587

588

// Create JWE using JWK

589

Jwk<PublicKey> encryptionJwk = Jwts.JWK.builder(encryptionPublicKey)

590

.id("encryption-key-1")

591

.algorithm("RSA-OAEP")

592

.build();

593

594

String jwe = Jwts.builder()

595

.subject("user")

596

.header()

597

.keyId(encryptionJwk.getId())

598

.and()

599

.encryptWith(encryptionJwk.toKey(), Jwts.KEY.RSA_OAEP, Jwts.ENC.A256GCM)

600

.compact();

601

```

602

603

### JWK-based Key Resolution

604

605

```java { .api }

606

// Key locator using JWK Set

607

JwkSet keySet = loadJwkSet();

608

609

Locator<Key> jwkLocator = header -> {

610

String keyId = header.getKeyId();

611

612

return keySet.getKeys().stream()

613

.filter(jwk -> keyId.equals(jwk.getId()))

614

.map(Jwk::toKey)

615

.findFirst()

616

.orElseThrow(() -> new SecurityException("Key not found: " + keyId));

617

};

618

619

JwtParser jwkParser = Jwts.parser()

620

.keyLocator(jwkLocator)

621

.build();

622

```

623

624

## Advanced JWK Features

625

626

### Custom JWK Parameters

627

628

```java { .api }

629

// JWK with custom parameters

630

Jwk<SecretKey> customParamJwk = Jwts.JWK.builder(secretKey)

631

.id("custom-key")

632

.algorithm("HS256")

633

.add("custom-param", "custom-value")

634

.add("version", "1.0")

635

.add("environment", "production")

636

.build();

637

638

// Access custom parameters

639

String customValue = customParamJwk.get("custom-param");

640

String version = customParamJwk.get("version");

641

```

642

643

### JWK Validation

644

645

```java { .api }

646

// Validate JWK structure and content

647

public static boolean validateJwk(Jwk<?> jwk) {

648

// Check required fields

649

if (jwk.getId() == null || jwk.getId().isEmpty()) {

650

return false;

651

}

652

653

// Validate key operations

654

Set<KeyOperation> operations = jwk.getOperations();

655

if (operations != null && !operations.isEmpty()) {

656

// Ensure operations are compatible with key type

657

Key key = jwk.toKey();

658

if (key instanceof SecretKey) {

659

// Validate symmetric key operations

660

return operations.stream().allMatch(op ->

661

op == KeyOperation.SIGN ||

662

op == KeyOperation.VERIFY ||

663

op == KeyOperation.ENCRYPT ||

664

op == KeyOperation.DECRYPT ||

665

op == KeyOperation.WRAP_KEY ||

666

op == KeyOperation.UNWRAP_KEY

667

);

668

}

669

}

670

671

return true;

672

}

673

674

// Validate JWK Set

675

public static boolean validateJwkSet(JwkSet jwkSet) {

676

return jwkSet.getKeys().stream()

677

.allMatch(jwk -> validateJwk(jwk));

678

}

679

```

680

681

The JWK Support implementation provides comprehensive, RFC 7517-compliant JSON Web Key functionality with full support for all standard key types, operations, X.509 integration, and seamless JWT/JWS/JWE integration.