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.