0
# Utilities
1
2
The Utilities functionality in JJWT Implementation provides essential supporting components including cryptographic key utilities, Base64URL encoding/decoding, stream processing, service discovery, parameter validation, and registry management. These utilities enable the core JWT functionality while providing extensible infrastructure for custom implementations.
3
4
## Key Generation and Management
5
6
### Keys Utility Class
7
8
The `Keys` utility class provides secure key generation and management operations for JWT cryptographic operations.
9
10
```java { .api }
11
import io.jsonwebtoken.security.Keys;
12
import io.jsonwebtoken.security.Password;
13
import io.jsonwebtoken.security.SecretKeyBuilder;
14
import io.jsonwebtoken.security.PrivateKeyBuilder;
15
import javax.crypto.SecretKey;
16
import java.security.KeyPair;
17
import java.security.PrivateKey;
18
import java.security.Provider;
19
20
/**
21
* Utility class for securely generating SecretKeys and KeyPairs.
22
*/
23
public final class Keys {
24
25
/**
26
* Creates a new SecretKey instance for use with HMAC-SHA algorithms based on the specified key byte array.
27
* @param bytes the key byte array
28
* @return a new SecretKey instance for use with HMAC-SHA algorithms
29
* @throws WeakKeyException if the key byte array length is less than 256 bits (32 bytes)
30
*/
31
public static SecretKey hmacShaKeyFor(byte[] bytes) throws WeakKeyException;
32
33
/**
34
* Returns a new Password instance suitable for use with password-based key derivation algorithms.
35
* @param password the raw password character array to clone
36
* @return a new Password instance that wraps a new clone of the specified password character array
37
*/
38
public static Password password(char[] password);
39
40
/**
41
* Returns a SecretKeyBuilder that produces the specified key, allowing association with a Provider.
42
* @param key the secret key to use for cryptographic operations
43
* @return a new SecretKeyBuilder that produces the specified key
44
*/
45
public static SecretKeyBuilder builder(SecretKey key);
46
47
/**
48
* Returns a PrivateKeyBuilder that produces the specified key, allowing association with a PublicKey or Provider.
49
* @param key the private key to use for cryptographic operations
50
* @return a new PrivateKeyBuilder that produces the specified private key
51
*/
52
public static PrivateKeyBuilder builder(PrivateKey key);
53
}
54
```
55
56
### HMAC Key Creation
57
58
```java { .api }
59
// Create HMAC key from byte array
60
byte[] keyBytes = generateSecureRandomBytes(32); // 256 bits for HS256
61
SecretKey hmacKey = Keys.hmacShaKeyFor(keyBytes);
62
63
// Key strength validation - automatically selects appropriate HMAC algorithm
64
byte[] strongKeyBytes = generateSecureRandomBytes(64); // 512 bits
65
SecretKey strongKey = Keys.hmacShaKeyFor(strongKeyBytes); // Returns HmacSHA512
66
67
// Weak key handling
68
try {
69
byte[] weakKeyBytes = generateSecureRandomBytes(16); // Only 128 bits
70
SecretKey weakKey = Keys.hmacShaKeyFor(weakKeyBytes);
71
} catch (WeakKeyException e) {
72
// Handle insufficient key strength
73
System.err.println("Key too weak: " + e.getMessage());
74
// Use algorithm-specific key generation instead
75
SecretKey safeKey = Jwts.SIG.HS256.key().build();
76
}
77
```
78
79
### Password-Based Key Derivation
80
81
```java { .api }
82
// Create password for PBKDF2 operations
83
char[] passwordChars = "secure-password".toCharArray();
84
Password password = Keys.password(passwordChars);
85
86
// Use with password-based algorithms
87
SecretKey derivedKey = Jwts.KEY.PBES2_HS256_A128KW.key()
88
.password(password)
89
.build();
90
91
// Clear password after use for security
92
Arrays.fill(passwordChars, '\0');
93
password.destroy(); // Clear internal password copy
94
```
95
96
### Provider-Specific Key Handling
97
98
```java { .api }
99
// Associate secret key with specific JCA provider
100
Provider hsm = Security.getProvider("SunPKCS11-HSM");
101
SecretKey hsmKey = getKeyFromHSM(); // Hypothetical HSM key
102
103
SecretKey providerKey = Keys.builder(hsmKey)
104
.provider(hsm)
105
.build();
106
107
// Associate private key with public key and provider
108
PrivateKey privateKey = getPrivateKeyFromHSM();
109
PublicKey publicKey = getCorrespondingPublicKey();
110
111
PrivateKey enhancedPrivateKey = Keys.builder(privateKey)
112
.publicKey(publicKey)
113
.provider(hsm)
114
.build();
115
116
// Use in JWT operations
117
String jwt = Jwts.builder()
118
.subject("user")
119
.signWith(enhancedPrivateKey)
120
.compact();
121
```
122
123
### Modern Algorithm Integration
124
125
```java { .api }
126
// Recommended approach: Use algorithm-specific key generation
127
// Instead of Keys.secretKeyFor(SignatureAlgorithm.HS256) [deprecated]
128
SecretKey hs256Key = Jwts.SIG.HS256.key().build();
129
SecretKey hs384Key = Jwts.SIG.HS384.key().build();
130
SecretKey hs512Key = Jwts.SIG.HS512.key().build();
131
132
// Instead of Keys.keyPairFor(SignatureAlgorithm.RS256) [deprecated]
133
KeyPair rs256KeyPair = Jwts.SIG.RS256.keyPair().build();
134
KeyPair ecKeyPair = Jwts.SIG.ES256.keyPair().build();
135
KeyPair eddsaKeyPair = Jwts.SIG.EdDSA.keyPair().build();
136
137
// With custom provider and secure random
138
Provider customProvider = getCustomProvider();
139
SecureRandom secureRandom = getSecureRandom();
140
141
SecretKey customKey = Jwts.SIG.HS256.key()
142
.provider(customProvider)
143
.random(secureRandom)
144
.build();
145
```
146
147
## Base64URL Codec
148
149
### Base64UrlCodec Class
150
151
RFC 4648 compliant Base64URL encoding and decoding without padding.
152
153
```java { .api }
154
import io.jsonwebtoken.impl.Base64UrlCodec;
155
import java.nio.charset.StandardCharsets;
156
157
// Create codec instance
158
Base64UrlCodec codec = new Base64UrlCodec();
159
160
// Encode string to Base64URL
161
String text = "Hello, World!";
162
String encoded = codec.encode(text);
163
// Result: "SGVsbG8sIFdvcmxkIQ" (no padding)
164
165
// Encode byte array to Base64URL
166
byte[] data = "Binary data".getBytes(StandardCharsets.UTF_8);
167
String encodedBytes = codec.encode(data);
168
169
// Decode Base64URL to string
170
String decoded = codec.decodeToString(encoded);
171
// Result: "Hello, World!"
172
173
// Decode Base64URL to byte array
174
byte[] decodedBytes = codec.decode(encodedBytes);
175
String recovered = new String(decodedBytes, StandardCharsets.UTF_8);
176
```
177
178
### Base64URL vs Standard Base64
179
180
```java { .api }
181
// Demonstrate Base64URL differences
182
String testData = "Test data with special characters: +/=";
183
byte[] bytes = testData.getBytes(StandardCharsets.UTF_8);
184
185
// Standard Base64 (with padding and +/= characters)
186
String standardB64 = Base64.getEncoder().encodeToString(bytes);
187
// Contains: +, /, = characters
188
189
// Base64URL (no padding, URL-safe characters)
190
Base64UrlCodec urlCodec = new Base64UrlCodec();
191
String base64Url = urlCodec.encode(bytes);
192
// Uses: -, _ instead of +, / and no padding
193
194
// URL safety demonstration
195
String urlWithStandardB64 = "https://api.example.com/token?jwt=" + standardB64;
196
// Problematic: contains +/= that need URL encoding
197
198
String urlWithBase64Url = "https://api.example.com/token?jwt=" + base64Url;
199
// Safe: no characters requiring URL encoding
200
```
201
202
### Advanced Base64URL Operations
203
204
```java { .api }
205
// Handle different input types
206
Base64UrlCodec codec = new Base64UrlCodec();
207
208
// Encode various data types
209
String jsonData = "{\"sub\":\"user\",\"exp\":1234567890}";
210
String encodedJson = codec.encode(jsonData);
211
212
byte[] binaryData = {0x48, 0x65, 0x6C, 0x6C, 0x6F}; // "Hello" in bytes
213
String encodedBinary = codec.encode(binaryData);
214
215
// Large data handling
216
byte[] largeData = generateLargeByteArray(10000);
217
String encodedLarge = codec.encode(largeData);
218
byte[] decodedLarge = codec.decode(encodedLarge);
219
220
// Verify round-trip integrity
221
boolean integrityCheck = Arrays.equals(largeData, decodedLarge);
222
223
// Error handling
224
try {
225
String invalidBase64Url = "Invalid!!!Base64URL";
226
byte[] result = codec.decode(invalidBase64Url);
227
} catch (IllegalArgumentException e) {
228
// Handle invalid Base64URL input
229
log.error("Invalid Base64URL input", e);
230
}
231
```
232
233
## Stream Processing
234
235
### Streams Utility Class
236
237
Memory-efficient stream operations for handling large payloads.
238
239
```java { .api }
240
import io.jsonwebtoken.impl.io.Streams;
241
import java.io.InputStream;
242
import java.io.OutputStream;
243
import java.io.ByteArrayInputStream;
244
import java.io.ByteArrayOutputStream;
245
import java.io.FileInputStream;
246
import java.io.FileOutputStream;
247
248
// Copy streams efficiently
249
InputStream source = new FileInputStream("large-file.json");
250
OutputStream destination = new ByteArrayOutputStream();
251
252
// Copy with automatic resource management
253
long bytesCopied = Streams.copy(source, destination);
254
255
// Convert stream to byte array
256
InputStream dataStream = new ByteArrayInputStream("Stream data".getBytes());
257
byte[] streamBytes = Streams.bytes(dataStream);
258
259
// Handle large streams without loading everything into memory
260
InputStream largeStream = new FileInputStream("very-large-file.dat");
261
OutputStream output = new FileOutputStream("output-file.dat");
262
Streams.copy(largeStream, output);
263
```
264
265
### Stream-based JWT Operations
266
267
```java { .api }
268
// Create JWT from large stream content
269
InputStream largePayload = new FileInputStream("large-document.json");
270
271
String streamJwt = Jwts.builder()
272
.content(largePayload) // Stream directly without loading into memory
273
.header()
274
.contentType("application/json")
275
.and()
276
.signWith(secretKey)
277
.compact();
278
279
// Parse JWT with stream content
280
JwtParser parser = Jwts.parser()
281
.verifyWith(secretKey)
282
.build();
283
284
Jws<InputStream> streamJws = parser.parseSignedContent(streamJwt);
285
InputStream contentStream = streamJws.getPayload();
286
287
// Process stream content efficiently
288
try (InputStream content = contentStream) {
289
byte[] data = Streams.bytes(content);
290
processLargeData(data);
291
}
292
```
293
294
### Memory-Efficient Operations
295
296
```java { .api }
297
// Process large JWT content without memory issues
298
public static void processLargeJwtContent(String jwt, SecretKey key) {
299
JwtParser parser = Jwts.parser()
300
.verifyWith(key)
301
.build();
302
303
// Parse to stream to avoid loading entire payload
304
Jws<InputStream> jws = parser.parseSignedContent(jwt);
305
306
try (InputStream content = jws.getPayload()) {
307
// Process in chunks to manage memory
308
byte[] buffer = new byte[8192]; // 8KB buffer
309
int bytesRead;
310
311
while ((bytesRead = content.read(buffer)) != -1) {
312
processChunk(buffer, bytesRead);
313
}
314
} catch (IOException e) {
315
throw new RuntimeException("Failed to process JWT content", e);
316
}
317
}
318
319
private static void processChunk(byte[] buffer, int length) {
320
// Process data chunk without storing entire payload
321
String chunk = new String(buffer, 0, length, StandardCharsets.UTF_8);
322
// ... process chunk
323
}
324
```
325
326
## Service Discovery
327
328
### Services Utility Class
329
330
Service loader utility for implementation discovery and plugin architecture.
331
332
```java { .api }
333
import io.jsonwebtoken.impl.lang.Services;
334
import io.jsonwebtoken.io.Serializer;
335
import io.jsonwebtoken.io.Deserializer;
336
import io.jsonwebtoken.security.SecureDigestAlgorithm;
337
import java.util.Iterator;
338
import java.util.function.Predicate;
339
340
// Get first available service implementation
341
Serializer<Map<String, ?>> serializer = Services.get(Serializer.class);
342
343
// Get service with specific criteria
344
Predicate<Deserializer<Map<String, ?>>> jsonFilter =
345
deserializer -> deserializer.getClass().getSimpleName().contains("Jackson");
346
347
Deserializer<Map<String, ?>> jsonDeserializer =
348
Services.get(Deserializer.class, jsonFilter);
349
350
// Get all service implementations
351
Iterator<SecureDigestAlgorithm> algorithms =
352
Services.iterator(SecureDigestAlgorithm.class);
353
354
// Service availability check
355
boolean hasJacksonSupport = Services.get(Serializer.class) != null;
356
```
357
358
### Service Loading Examples
359
360
```java { .api }
361
// Load custom algorithm implementations
362
public static void loadCustomAlgorithms() {
363
// Service loader automatically discovers implementations
364
Iterator<SecureDigestAlgorithm> customAlgorithms =
365
Services.iterator(SecureDigestAlgorithm.class);
366
367
while (customAlgorithms.hasNext()) {
368
SecureDigestAlgorithm algorithm = customAlgorithms.next();
369
370
if (algorithm.getId().startsWith("CUSTOM_")) {
371
// Register custom algorithm
372
registerCustomAlgorithm(algorithm);
373
}
374
}
375
}
376
377
// Load serialization providers
378
public static Serializer<Map<String, ?>> loadBestSerializer() {
379
// Try Jackson first
380
Serializer<Map<String, ?>> jacksonSerializer =
381
Services.get(Serializer.class, s ->
382
s.getClass().getName().contains("jackson"));
383
384
if (jacksonSerializer != null) {
385
return jacksonSerializer;
386
}
387
388
// Fallback to any available serializer
389
return Services.get(Serializer.class);
390
}
391
392
// Service caching and reload
393
Services.reload(); // Clear service cache and reload
394
```
395
396
## Registry Management
397
398
### DefaultRegistry Class
399
400
Generic registry implementation for algorithm and component collections.
401
402
```java { .api }
403
import io.jsonwebtoken.impl.lang.DefaultRegistry;
404
import io.jsonwebtoken.security.SecureDigestAlgorithm;
405
import io.jsonwebtoken.CompressionAlgorithm;
406
407
// Create custom algorithm registry
408
DefaultRegistry<SecureDigestAlgorithm<?, ?>> customSigRegistry =
409
new DefaultRegistry<>("Custom Signature Algorithms");
410
411
// Add algorithms to registry
412
SecureDigestAlgorithm<?, ?> customAlgorithm = createCustomAlgorithm();
413
customSigRegistry.add(customAlgorithm);
414
415
// Registry operations
416
boolean hasAlgorithm = customSigRegistry.containsKey("CUSTOM_HS256");
417
SecureDigestAlgorithm<?, ?> algorithm = customSigRegistry.get("CUSTOM_HS256");
418
Set<String> algorithmIds = customSigRegistry.keySet();
419
Collection<SecureDigestAlgorithm<?, ?>> allAlgorithms = customSigRegistry.values();
420
421
// Registry with validation
422
SecureDigestAlgorithm<?, ?> validatedAlgorithm =
423
customSigRegistry.forKey("CUSTOM_HS256"); // Throws if not found
424
```
425
426
### Registry Integration
427
428
```java { .api }
429
// Extend existing registries
430
public class ExtendedSignatureRegistry extends DefaultRegistry<SecureDigestAlgorithm<?, ?>> {
431
432
public ExtendedSignatureRegistry() {
433
super("Extended Signature Algorithms");
434
435
// Add all standard algorithms
436
addAll(Jwts.SIG.get().values());
437
438
// Add custom algorithms
439
add(new CustomHMACAlgorithm("CUSTOM_HS256", 256));
440
add(new CustomRSAAlgorithm("CUSTOM_RS256"));
441
add(new CustomECAlgorithm("CUSTOM_ES256"));
442
}
443
}
444
445
// Use extended registry
446
ExtendedSignatureRegistry extendedSig = new ExtendedSignatureRegistry();
447
448
JwtParser extendedParser = Jwts.parser()
449
.verifyWith(secretKey)
450
.sig()
451
.add(extendedSig.get("CUSTOM_HS256"))
452
.and()
453
.build();
454
```
455
456
## Parameter Validation
457
458
### Parameters Utility Class
459
460
Type-safe parameter validation and assertion utilities.
461
462
```java { .api }
463
import io.jsonwebtoken.impl.lang.Parameters;
464
465
// Parameter validation examples
466
public static SecretKey validateSecretKey(SecretKey key, String algorithmId) {
467
// Check for null
468
Parameters.notNull(key, "Secret key cannot be null");
469
470
// Check key algorithm
471
String keyAlgorithm = key.getAlgorithm();
472
Parameters.notEmpty(keyAlgorithm, "Key algorithm cannot be empty");
473
474
// Validate key length
475
byte[] encoded = key.getEncoded();
476
Parameters.notEmpty(encoded, "Key must be encodable");
477
478
int bitLength = encoded.length * 8;
479
Parameters.gt(bitLength, 255, "Key must be at least 256 bits");
480
481
return key;
482
}
483
484
// String parameter validation
485
public static String validateKeyId(String keyId) {
486
Parameters.notEmpty(keyId, "Key ID cannot be null or empty");
487
Parameters.maxLength(keyId, 255, "Key ID too long");
488
489
// Custom validation
490
if (!keyId.matches("^[a-zA-Z0-9_-]+$")) {
491
throw new IllegalArgumentException("Invalid key ID format");
492
}
493
494
return keyId;
495
}
496
497
// Collection validation
498
public static <T> List<T> validateList(List<T> list, String paramName) {
499
Parameters.notNull(list, paramName + " cannot be null");
500
Parameters.notEmpty(list, paramName + " cannot be empty");
501
502
// Validate elements
503
for (int i = 0; i < list.size(); i++) {
504
Parameters.notNull(list.get(i), paramName + "[" + i + "] cannot be null");
505
}
506
507
return list;
508
}
509
```
510
511
### Advanced Parameter Validation
512
513
```java { .api }
514
// Complex parameter validation patterns
515
public static KeyPair validateKeyPairForAlgorithm(KeyPair keyPair, String algorithm) {
516
Parameters.notNull(keyPair, "KeyPair cannot be null");
517
Parameters.notNull(keyPair.getPublic(), "Public key cannot be null");
518
Parameters.notNull(keyPair.getPrivate(), "Private key cannot be null");
519
520
// Algorithm-specific validation
521
if (algorithm.startsWith("RS") || algorithm.startsWith("PS")) {
522
// RSA validation
523
Parameters.eq(keyPair.getPublic().getAlgorithm(), "RSA",
524
"Algorithm " + algorithm + " requires RSA keys");
525
526
// Check key size
527
RSAPublicKey rsaPublic = (RSAPublicKey) keyPair.getPublic();
528
int keySize = rsaPublic.getModulus().bitLength();
529
Parameters.gte(keySize, 2048, "RSA keys must be at least 2048 bits");
530
531
} else if (algorithm.startsWith("ES")) {
532
// EC validation
533
Parameters.eq(keyPair.getPublic().getAlgorithm(), "EC",
534
"Algorithm " + algorithm + " requires EC keys");
535
}
536
537
return keyPair;
538
}
539
540
// Conditional validation
541
public static void validateJWEParameters(Key kek, String keyAlg, String encAlg) {
542
Parameters.notNull(kek, "Key encryption key cannot be null");
543
Parameters.notEmpty(keyAlg, "Key algorithm cannot be empty");
544
Parameters.notEmpty(encAlg, "Encryption algorithm cannot be empty");
545
546
// Validate key compatibility with algorithm
547
if (keyAlg.startsWith("RSA")) {
548
Parameters.isTrue(kek instanceof PublicKey || kek instanceof PrivateKey,
549
"RSA algorithms require asymmetric keys");
550
} else if (keyAlg.contains("KW")) {
551
Parameters.isTrue(kek instanceof SecretKey,
552
"Key wrap algorithms require symmetric keys");
553
}
554
}
555
```
556
557
## Function Utilities
558
559
### Functions Class
560
561
Common functional utilities and constants.
562
563
```java { .api }
564
import io.jsonwebtoken.impl.lang.Functions;
565
import java.util.function.Function;
566
import java.util.function.Predicate;
567
568
// Common function constants and utilities
569
// (Specific implementations depend on the Functions class content)
570
571
// Example usage patterns for functional operations
572
public static <T> List<T> filterAndTransform(List<T> input,
573
Predicate<T> filter,
574
Function<T, T> transformer) {
575
return input.stream()
576
.filter(filter)
577
.map(transformer)
578
.collect(Collectors.toList());
579
}
580
581
// Functional JWT processing
582
public static List<Claims> extractClaimsFromTokens(List<String> tokens,
583
SecretKey key) {
584
JwtParser parser = Jwts.parser().verifyWith(key).build();
585
586
return tokens.stream()
587
.map(token -> {
588
try {
589
return parser.parseSignedClaims(token).getPayload();
590
} catch (Exception e) {
591
return null; // Filter out invalid tokens
592
}
593
})
594
.filter(Objects::nonNull)
595
.collect(Collectors.toList());
596
}
597
```
598
599
## Clock Utilities
600
601
### FixedClock Implementation
602
603
Testing utility for fixed-time scenarios.
604
605
```java { .api }
606
import io.jsonwebtoken.impl.FixedClock;
607
import io.jsonwebtoken.Clock;
608
import java.time.Instant;
609
import java.util.Date;
610
611
// Create fixed clock for testing
612
Date fixedTime = Date.from(Instant.parse("2024-01-01T12:00:00Z"));
613
Clock fixedClock = new FixedClock(fixedTime);
614
615
// Use fixed clock in JWT operations
616
JwtParser testParser = Jwts.parser()
617
.verifyWith(secretKey)
618
.clock(fixedClock) // All time-based validations use fixed time
619
.build();
620
621
// Create JWT with fixed issuedAt
622
String testJwt = Jwts.builder()
623
.subject("test-user")
624
.issuedAt(fixedClock.now()) // Uses fixed time
625
.expiration(new Date(fixedTime.getTime() + 3600000)) // 1 hour later
626
.signWith(secretKey)
627
.compact();
628
629
// Test time-based scenarios
630
public static void testTokenExpiration() {
631
Date now = Date.from(Instant.parse("2024-01-01T12:00:00Z"));
632
Date expiry = Date.from(Instant.parse("2024-01-01T13:00:00Z"));
633
634
// Create token
635
Clock createTime = new FixedClock(now);
636
String jwt = Jwts.builder()
637
.subject("user")
638
.issuedAt(createTime.now())
639
.expiration(expiry)
640
.signWith(secretKey)
641
.compact();
642
643
// Test parsing at different times
644
Clock beforeExpiry = new FixedClock(Date.from(Instant.parse("2024-01-01T12:30:00Z")));
645
Clock afterExpiry = new FixedClock(Date.from(Instant.parse("2024-01-01T14:00:00Z")));
646
647
// Should succeed
648
JwtParser validParser = Jwts.parser()
649
.verifyWith(secretKey)
650
.clock(beforeExpiry)
651
.build();
652
653
Claims validClaims = validParser.parseSignedClaims(jwt).getPayload();
654
655
// Should fail with ExpiredJwtException
656
JwtParser expiredParser = Jwts.parser()
657
.verifyWith(secretKey)
658
.clock(afterExpiry)
659
.build();
660
661
try {
662
expiredParser.parseSignedClaims(jwt);
663
fail("Should have thrown ExpiredJwtException");
664
} catch (ExpiredJwtException e) {
665
// Expected
666
}
667
}
668
```
669
670
## Performance Utilities
671
672
### Optimization Patterns
673
674
```java { .api }
675
// Reusable component caching
676
public class JWTUtilityCache {
677
private static final Base64UrlCodec CODEC = new Base64UrlCodec();
678
private static final Map<String, JwtParser> PARSER_CACHE = new ConcurrentHashMap<>();
679
680
public static String encodePayload(Object payload) {
681
String json = serializeToJson(payload);
682
return CODEC.encode(json);
683
}
684
685
public static JwtParser getCachedParser(String keyId, Key key) {
686
return PARSER_CACHE.computeIfAbsent(keyId, k ->
687
Jwts.parser().verifyWith(key).build()
688
);
689
}
690
691
public static void clearCache() {
692
PARSER_CACHE.clear();
693
}
694
}
695
696
// Batch processing utilities
697
public static Map<String, Claims> parseTokenBatch(List<String> tokens,
698
Map<String, Key> keyMap) {
699
return tokens.parallelStream()
700
.collect(Collectors.toConcurrentMap(
701
token -> token, // Use token as key
702
token -> {
703
try {
704
// Extract key ID from header
705
String keyId = extractKeyIdFromToken(token);
706
Key key = keyMap.get(keyId);
707
708
if (key != null) {
709
JwtParser parser = JWTUtilityCache.getCachedParser(keyId, key);
710
return parser.parseSignedClaims(token).getPayload();
711
}
712
return null;
713
} catch (Exception e) {
714
return null; // Skip invalid tokens
715
}
716
},
717
(existing, replacement) -> existing, // Keep first valid result
718
ConcurrentHashMap::new
719
)).entrySet().stream()
720
.filter(entry -> entry.getValue() != null)
721
.collect(Collectors.toMap(
722
Map.Entry::getKey,
723
Map.Entry::getValue
724
));
725
}
726
```
727
728
## Input/Output (IO) Package
729
730
The IO package provides interfaces and utilities for encoding, decoding, serialization, and deserialization operations used throughout JJWT.
731
732
### Core IO Interfaces
733
734
```java { .api }
735
import io.jsonwebtoken.io.Encoder;
736
import io.jsonwebtoken.io.Decoder;
737
import io.jsonwebtoken.io.Serializer;
738
import io.jsonwebtoken.io.Deserializer;
739
import io.jsonwebtoken.io.Parser;
740
import java.io.InputStream;
741
import java.io.OutputStream;
742
import java.util.Map;
743
744
/**
745
* An encoder converts data of one type into another formatted data value.
746
*/
747
public interface Encoder<T, R> {
748
/**
749
* Convert the specified data into another formatted data value.
750
* @param t the data to convert
751
* @return the converted data
752
*/
753
R encode(T t);
754
}
755
756
/**
757
* A decoder converts formatted data of one type into another data type.
758
*/
759
public interface Decoder<T, R> {
760
/**
761
* Convert the specified formatted data into another data value.
762
* @param t the formatted data to convert
763
* @return the converted data
764
*/
765
R decode(T t);
766
}
767
768
/**
769
* A Serializer is able to convert a Java object into a formatted byte stream.
770
*/
771
public interface Serializer<T> {
772
/**
773
* Converts the specified Java object into a formatted data byte array.
774
* @param t the Java object to convert
775
* @return the formatted data as a byte array
776
*/
777
byte[] serialize(T t);
778
779
/**
780
* Converts the specified Java object into a formatted data stream.
781
* @param t the Java object to convert
782
* @param out the OutputStream to write to
783
*/
784
void serialize(T t, OutputStream out);
785
}
786
787
/**
788
* A Deserializer is able to convert formatted byte data back into a Java object.
789
*/
790
public interface Deserializer<T> {
791
/**
792
* Convert the specified formatted data byte array into a Java object.
793
* @param data the formatted data bytes
794
* @return the reconstituted Java object
795
*/
796
T deserialize(byte[] data);
797
798
/**
799
* Convert the specified formatted data stream into a Java object.
800
* @param in the InputStream to read from
801
* @return the reconstituted Java object
802
*/
803
T deserialize(InputStream in);
804
}
805
806
/**
807
* A Parser converts data from one format to another.
808
*/
809
public interface Parser<T> {
810
/**
811
* Parse the specified value into the expected Java object.
812
* @param value the formatted value to parse
813
* @return the parsed Java object
814
*/
815
T parse(CharSequence value);
816
}
817
```
818
819
### Base64 Encoding and Decoding
820
821
```java { .api }
822
import io.jsonwebtoken.io.Encoders;
823
import io.jsonwebtoken.io.Decoders;
824
import io.jsonwebtoken.io.Base64Encoder;
825
import io.jsonwebtoken.io.Base64Decoder;
826
827
// Standard Base64 encoding/decoding
828
Base64Encoder base64Encoder = Encoders.BASE64;
829
Base64Decoder base64Decoder = Decoders.BASE64;
830
831
String originalData = "Hello, World!";
832
String encoded = base64Encoder.encode(originalData);
833
String decoded = base64Decoder.decode(encoded);
834
835
// Base64URL encoding/decoding (JWT standard)
836
Base64Encoder base64UrlEncoder = Encoders.BASE64URL;
837
Base64Decoder base64UrlDecoder = Decoders.BASE64URL;
838
839
String urlSafeEncoded = base64UrlEncoder.encode(originalData);
840
String urlSafeDecoded = base64UrlDecoder.decode(urlSafeEncoded);
841
842
// Binary data encoding
843
byte[] binaryData = "Binary content".getBytes(StandardCharsets.UTF_8);
844
String encodedBinary = base64UrlEncoder.encode(binaryData);
845
byte[] decodedBinary = base64UrlDecoder.decodeToBytes(encodedBinary);
846
```
847
848
### JSON Serialization
849
850
```java { .api }
851
// Use with JWT builder for custom JSON serialization
852
Map<String, Object> customPayload = new HashMap<>();
853
customPayload.put("user_id", 12345);
854
customPayload.put("roles", Arrays.asList("admin", "user"));
855
customPayload.put("metadata", Map.of("department", "engineering"));
856
857
Serializer<Map<String, ?>> jsonSerializer = getJsonSerializer();
858
859
String jwt = Jwts.builder()
860
.claims(customPayload)
861
.json(jsonSerializer) // Use custom JSON serializer
862
.signWith(secretKey)
863
.compact();
864
865
// Use with JWT parser for custom JSON deserialization
866
Deserializer<Map<String, ?>> jsonDeserializer = getJsonDeserializer();
867
868
JwtParser parser = Jwts.parser()
869
.verifyWith(secretKey)
870
.json(jsonDeserializer) // Use custom JSON deserializer
871
.build();
872
873
Jws<Claims> jws = parser.parseSignedClaims(jwt);
874
```
875
876
### Stream-Based Operations
877
878
```java { .api }
879
// Custom serializer for large payloads
880
public class StreamingJsonSerializer implements Serializer<Map<String, ?>> {
881
882
@Override
883
public byte[] serialize(Map<String, ?> map) {
884
ByteArrayOutputStream baos = new ByteArrayOutputStream();
885
serialize(map, baos);
886
return baos.toByteArray();
887
}
888
889
@Override
890
public void serialize(Map<String, ?> map, OutputStream out) {
891
try (JsonGenerator generator = createJsonGenerator(out)) {
892
generator.writeStartObject();
893
for (Map.Entry<String, ?> entry : map.entrySet()) {
894
generator.writeObjectField(entry.getKey(), entry.getValue());
895
}
896
generator.writeEndObject();
897
} catch (IOException e) {
898
throw new SerializationException("JSON serialization failed", e);
899
}
900
}
901
}
902
903
// Custom deserializer for streaming
904
public class StreamingJsonDeserializer implements Deserializer<Map<String, ?>> {
905
906
@Override
907
public Map<String, ?> deserialize(byte[] data) {
908
return deserialize(new ByteArrayInputStream(data));
909
}
910
911
@Override
912
public Map<String, ?> deserialize(InputStream in) {
913
try (JsonParser parser = createJsonParser(in)) {
914
return parseJsonObject(parser);
915
} catch (IOException e) {
916
throw new DeserializationException("JSON deserialization failed", e);
917
}
918
}
919
}
920
```
921
922
### IO Exception Types
923
924
```java { .api }
925
import io.jsonwebtoken.io.IOException;
926
import io.jsonwebtoken.io.SerializationException;
927
import io.jsonwebtoken.io.DeserializationException;
928
import io.jsonwebtoken.io.DecodingException;
929
930
/**
931
* Base IO exception for all input/output related errors.
932
*/
933
public class IOException extends RuntimeException;
934
935
/**
936
* Exception thrown when object serialization fails.
937
*/
938
public class SerializationException extends IOException;
939
940
/**
941
* Exception thrown when data deserialization fails.
942
*/
943
public class DeserializationException extends IOException;
944
945
/**
946
* Exception thrown when data decoding fails.
947
*/
948
public class DecodingException extends IOException;
949
950
// Exception handling in custom serializers
951
public class SafeJsonSerializer implements Serializer<Map<String, ?>> {
952
953
@Override
954
public byte[] serialize(Map<String, ?> map) {
955
try {
956
return performSerialization(map);
957
} catch (Exception e) {
958
throw new SerializationException("Failed to serialize JSON: " + e.getMessage(), e);
959
}
960
}
961
962
@Override
963
public void serialize(Map<String, ?> map, OutputStream out) {
964
try {
965
performStreamSerialization(map, out);
966
} catch (Exception e) {
967
throw new SerializationException("Failed to serialize JSON to stream: " + e.getMessage(), e);
968
}
969
}
970
}
971
```
972
973
### Custom Parser Implementation
974
975
```java { .api }
976
// Custom parser for special JWT formats
977
public class CustomJwtFormatParser implements Parser<Jwt<?, ?>> {
978
979
private final JwtParser delegate;
980
981
public CustomJwtFormatParser(JwtParser delegate) {
982
this.delegate = delegate;
983
}
984
985
@Override
986
public Jwt<?, ?> parse(CharSequence value) {
987
// Pre-process custom format
988
String standardJwt = convertCustomFormatToStandard(value.toString());
989
990
// Delegate to standard parser
991
return delegate.parse(standardJwt);
992
}
993
994
private String convertCustomFormatToStandard(String customFormat) {
995
// Implementation-specific conversion logic
996
if (customFormat.startsWith("CUSTOM:")) {
997
return customFormat.substring(7); // Remove custom prefix
998
}
999
return customFormat;
1000
}
1001
}
1002
1003
// Usage with custom parser
1004
Parser<Jwt<?, ?>> customParser = new CustomJwtFormatParser(
1005
Jwts.parser()
1006
.verifyWith(secretKey)
1007
.build()
1008
);
1009
1010
Jwt<?, ?> jwt = customParser.parse("CUSTOM:eyJ0eXAiOiJKV1QiLCJhbGc...");
1011
```
1012
1013
## Supporting Type Definitions
1014
1015
### Core Interface Types
1016
1017
```java { .api }
1018
import io.jsonwebtoken.Clock;
1019
import io.jsonwebtoken.Locator;
1020
import io.jsonwebtoken.Identifiable;
1021
import io.jsonwebtoken.security.SecretKeyBuilder;
1022
import io.jsonwebtoken.security.PrivateKeyBuilder;
1023
import io.jsonwebtoken.security.Password;
1024
import java.util.Date;
1025
import java.security.Key;
1026
1027
/**
1028
* Clock interface for time operations.
1029
*/
1030
public interface Clock {
1031
/**
1032
* Returns the current time as a Date.
1033
* @return the current time
1034
*/
1035
Date now();
1036
}
1037
1038
/**
1039
* Locator interface for resource location operations.
1040
*/
1041
public interface Locator<T> {
1042
/**
1043
* Locates a resource based on the provided context.
1044
* @param header JWT header providing context
1045
* @return the located resource
1046
*/
1047
T locate(JwsHeader header);
1048
}
1049
1050
/**
1051
* Identifiable interface for objects with string identifiers.
1052
*/
1053
public interface Identifiable {
1054
/**
1055
* Returns the string identifier of this object.
1056
* @return the string identifier
1057
*/
1058
String getId();
1059
}
1060
1061
/**
1062
* Builder for SecretKey instances with provider support.
1063
*/
1064
public interface SecretKeyBuilder extends Builder<SecretKey> {
1065
/**
1066
* Associates a Provider that must be used with the key.
1067
* @param provider the JCA Provider
1068
* @return this builder for method chaining
1069
*/
1070
SecretKeyBuilder provider(Provider provider);
1071
}
1072
1073
/**
1074
* Builder for PrivateKey instances with public key and provider support.
1075
*/
1076
public interface PrivateKeyBuilder extends Builder<PrivateKey> {
1077
/**
1078
* Associates a public key with the private key.
1079
* @param publicKey the corresponding public key
1080
* @return this builder for method chaining
1081
*/
1082
PrivateKeyBuilder publicKey(PublicKey publicKey);
1083
1084
/**
1085
* Associates a Provider that must be used with the key.
1086
* @param provider the JCA Provider
1087
* @return this builder for method chaining
1088
*/
1089
PrivateKeyBuilder provider(Provider provider);
1090
}
1091
1092
/**
1093
* Password interface for password-based key derivation.
1094
*/
1095
public interface Password extends Key {
1096
/**
1097
* Returns the password as a character array.
1098
* @return the password characters
1099
*/
1100
char[] toCharArray();
1101
1102
/**
1103
* Destroys this password by clearing its internal state.
1104
*/
1105
void destroy();
1106
}
1107
```
1108
1109
### Visitor Pattern Types
1110
1111
```java { .api }
1112
import io.jsonwebtoken.JwtVisitor;
1113
import io.jsonwebtoken.SupportedJwtVisitor;
1114
1115
/**
1116
* Visitor interface for processing different JWT types.
1117
*/
1118
public interface JwtVisitor<T> {
1119
/**
1120
* Visit an unsecured JWT with content payload.
1121
* @param jwt the unsecured content JWT
1122
* @return visitor result
1123
*/
1124
T visit(Jwt<Header, byte[]> jwt);
1125
1126
/**
1127
* Visit an unsecured JWT with claims payload.
1128
* @param jwt the unsecured claims JWT
1129
* @return visitor result
1130
*/
1131
T visit(Jwt<Header, Claims> jwt);
1132
1133
/**
1134
* Visit a signed JWT with content payload.
1135
* @param jws the signed content JWT
1136
* @return visitor result
1137
*/
1138
T visit(Jws<byte[]> jws);
1139
1140
/**
1141
* Visit a signed JWT with claims payload.
1142
* @param jws the signed claims JWT
1143
* @return visitor result
1144
*/
1145
T visit(Jws<Claims> jws);
1146
1147
/**
1148
* Visit an encrypted JWT with content payload.
1149
* @param jwe the encrypted content JWT
1150
* @return visitor result
1151
*/
1152
T visit(Jwe<byte[]> jwe);
1153
1154
/**
1155
* Visit an encrypted JWT with claims payload.
1156
* @param jwe the encrypted claims JWT
1157
* @return visitor result
1158
*/
1159
T visit(Jwe<Claims> jwe);
1160
}
1161
1162
/**
1163
* Base visitor implementation with default unsupported operation responses.
1164
*/
1165
public abstract class SupportedJwtVisitor<T> implements JwtVisitor<T> {
1166
1167
/**
1168
* Default message for unsupported operations.
1169
*/
1170
public static final String UNSUPPORTED_MSG = "Unsupported JWT type.";
1171
1172
@Override
1173
public T visit(Jwt<Header, byte[]> jwt) {
1174
throw new UnsupportedJwtException(UNSUPPORTED_MSG);
1175
}
1176
1177
@Override
1178
public T visit(Jwt<Header, Claims> jwt) {
1179
throw new UnsupportedJwtException(UNSUPPORTED_MSG);
1180
}
1181
1182
@Override
1183
public T visit(Jws<byte[]> jws) {
1184
throw new UnsupportedJwtException(UNSUPPORTED_MSG);
1185
}
1186
1187
@Override
1188
public T visit(Jws<Claims> jws) {
1189
throw new UnsupportedJwtException(UNSUPPORTED_MSG);
1190
}
1191
1192
@Override
1193
public T visit(Jwe<byte[]> jwe) {
1194
throw new UnsupportedJwtException(UNSUPPORTED_MSG);
1195
}
1196
1197
@Override
1198
public T visit(Jwe<Claims> jwe) {
1199
throw new UnsupportedJwtException(UNSUPPORTED_MSG);
1200
}
1201
}
1202
1203
// Example visitor implementation
1204
public class JwtTypeExtractor extends SupportedJwtVisitor<String> {
1205
1206
@Override
1207
public String visit(Jwt<Header, Claims> jwt) {
1208
return "unsecured-claims";
1209
}
1210
1211
@Override
1212
public String visit(Jws<Claims> jws) {
1213
return "signed-claims";
1214
}
1215
1216
@Override
1217
public String visit(Jwe<Claims> jwe) {
1218
return "encrypted-claims";
1219
}
1220
}
1221
1222
// Usage with visitor pattern
1223
JwtTypeExtractor extractor = new JwtTypeExtractor();
1224
Jwt<?, ?> someJwt = parser.parse(token);
1225
String jwtType = someJwt.accept(extractor);
1226
```
1227
1228
### Registry and Collection Types
1229
1230
```java { .api }
1231
import io.jsonwebtoken.lang.Registry;
1232
import io.jsonwebtoken.lang.NestedCollection;
1233
1234
/**
1235
* Registry interface for algorithm and component lookup.
1236
*/
1237
public interface Registry<K, V> extends Map<K, V> {
1238
/**
1239
* Returns the value associated with the specified key.
1240
* @param key the key to look up
1241
* @return the associated value
1242
*/
1243
V forKey(K key);
1244
1245
/**
1246
* Returns all values in the registry.
1247
* @return collection of all values
1248
*/
1249
Collection<V> values();
1250
}
1251
1252
/**
1253
* Nested collection interface for fluent configuration.
1254
*/
1255
public interface NestedCollection<T, P> {
1256
/**
1257
* Add an item to the collection.
1258
* @param item the item to add
1259
* @return this collection for chaining
1260
*/
1261
NestedCollection<T, P> add(T item);
1262
1263
/**
1264
* Remove an item from the collection.
1265
* @param item the item to remove
1266
* @return this collection for chaining
1267
*/
1268
NestedCollection<T, P> remove(T item);
1269
1270
/**
1271
* Return to the parent builder.
1272
* @return the parent builder
1273
*/
1274
P and();
1275
}
1276
```
1277
1278
The Utilities implementation provides robust, performance-optimized supporting functionality that enables the core JWT operations while maintaining extensibility and proper error handling.