0
# Key Providers
1
2
Dynamic key resolution interfaces supporting key rotation, multi-tenant scenarios, and advanced key management patterns for RSA and ECDSA algorithms.
3
4
## Capabilities
5
6
### Key Provider Interface
7
8
Generic interface for providing cryptographic keys with support for key identification and dynamic resolution.
9
10
```java { .api }
11
/**
12
* Generic key provider interface for dynamic key resolution
13
* @param <U> Public key type (RSAPublicKey or ECPublicKey)
14
* @param <R> Private key type (RSAPrivateKey or ECPrivateKey)
15
*/
16
public interface KeyProvider<U, R> {
17
/**
18
* Getter for a Public Key instance using the received Key Id.
19
* @param keyId the Key Id specified in the JWT header
20
* @return a public key instance for verification or null if not found
21
*/
22
U getPublicKeyById(String keyId);
23
24
/**
25
* Getter for a Private Key instance used for signing JWTs.
26
* @return a private key instance for signing or null if not available
27
*/
28
R getPrivateKey();
29
30
/**
31
* Getter for the Id of the Private Key used for signing JWTs.
32
* @return the key id of the private key or null if not specified
33
*/
34
String getPrivateKeyId();
35
}
36
```
37
38
**Usage Examples:**
39
40
```java
41
import com.auth0.jwt.interfaces.KeyProvider;
42
import java.security.interfaces.RSAPublicKey;
43
import java.security.interfaces.RSAPrivateKey;
44
45
// Custom key provider implementation
46
public class CustomKeyProvider implements KeyProvider<RSAPublicKey, RSAPrivateKey> {
47
private Map<String, RSAPublicKey> publicKeys;
48
private RSAPrivateKey privateKey;
49
private String privateKeyId;
50
51
public CustomKeyProvider(Map<String, RSAPublicKey> publicKeys,
52
RSAPrivateKey privateKey,
53
String privateKeyId) {
54
this.publicKeys = publicKeys;
55
this.privateKey = privateKey;
56
this.privateKeyId = privateKeyId;
57
}
58
59
@Override
60
public RSAPublicKey getPublicKeyById(String keyId) {
61
return publicKeys.get(keyId);
62
}
63
64
@Override
65
public RSAPrivateKey getPrivateKey() {
66
return privateKey;
67
}
68
69
@Override
70
public String getPrivateKeyId() {
71
return privateKeyId;
72
}
73
}
74
```
75
76
### RSA Key Provider
77
78
Specialized key provider interface for RSA cryptographic operations.
79
80
```java { .api }
81
/**
82
* RSA Key Provider interface for providing RSA keys for JWT signing and verification.
83
* Extends the generic KeyProvider with RSA-specific key types.
84
*/
85
public interface RSAKeyProvider extends KeyProvider<RSAPublicKey, RSAPrivateKey> {
86
/**
87
* Getter for an RSA Public Key instance using the received Key Id.
88
* @param keyId the Key Id specified in the JWT header ("kid" claim)
89
* @return an RSAPublicKey instance for verification or null if not found
90
*/
91
RSAPublicKey getPublicKeyById(String keyId);
92
93
/**
94
* Getter for an RSA Private Key instance used for signing JWTs.
95
* @return an RSAPrivateKey instance for signing or null if not available
96
*/
97
RSAPrivateKey getPrivateKey();
98
99
/**
100
* Getter for the Id of the RSA Private Key used for signing JWTs.
101
* @return the key id of the private key or null if not specified
102
*/
103
String getPrivateKeyId();
104
}
105
```
106
107
**Usage Examples:**
108
109
```java
110
import com.auth0.jwt.interfaces.RSAKeyProvider;
111
import com.auth0.jwt.algorithms.Algorithm;
112
import java.security.interfaces.RSAPublicKey;
113
import java.security.interfaces.RSAPrivateKey;
114
import java.util.Map;
115
import java.util.HashMap;
116
117
// Simple RSA key provider implementation
118
public class SimpleRSAKeyProvider implements RSAKeyProvider {
119
private final Map<String, RSAPublicKey> publicKeys;
120
private final RSAPrivateKey privateKey;
121
private final String privateKeyId;
122
123
public SimpleRSAKeyProvider(RSAPrivateKey privateKey, String privateKeyId,
124
Map<String, RSAPublicKey> publicKeys) {
125
this.privateKey = privateKey;
126
this.privateKeyId = privateKeyId;
127
this.publicKeys = new HashMap<>(publicKeys);
128
}
129
130
@Override
131
public RSAPublicKey getPublicKeyById(String keyId) {
132
return publicKeys.get(keyId);
133
}
134
135
@Override
136
public RSAPrivateKey getPrivateKey() {
137
return privateKey;
138
}
139
140
@Override
141
public String getPrivateKeyId() {
142
return privateKeyId;
143
}
144
}
145
146
// Usage with Algorithm
147
Map<String, RSAPublicKey> publicKeys = new HashMap<>();
148
publicKeys.put("key-1", rsaPublicKey1);
149
publicKeys.put("key-2", rsaPublicKey2);
150
151
RSAKeyProvider keyProvider = new SimpleRSAKeyProvider(
152
rsaPrivateKey,
153
"key-1",
154
publicKeys
155
);
156
157
Algorithm algorithm = Algorithm.RSA256(keyProvider);
158
159
// Create JWT with key ID in header
160
String token = JWT.create()
161
.withKeyId("key-1")
162
.withIssuer("auth0")
163
.sign(algorithm);
164
165
// Verify JWT - key provider will be used to resolve public key by key ID
166
JWTVerifier verifier = JWT.require(algorithm).build();
167
DecodedJWT jwt = verifier.verify(token);
168
```
169
170
### ECDSA Key Provider
171
172
Specialized key provider interface for Elliptic Curve Digital Signature Algorithm operations.
173
174
```java { .api }
175
/**
176
* ECDSA Key Provider interface for providing EC keys for JWT signing and verification.
177
* Extends the generic KeyProvider with ECDSA-specific key types.
178
*/
179
public interface ECDSAKeyProvider extends KeyProvider<ECPublicKey, ECPrivateKey> {
180
/**
181
* Getter for an EC Public Key instance using the received Key Id.
182
* @param keyId the Key Id specified in the JWT header ("kid" claim)
183
* @return an ECPublicKey instance for verification or null if not found
184
*/
185
ECPublicKey getPublicKeyById(String keyId);
186
187
/**
188
* Getter for an EC Private Key instance used for signing JWTs.
189
* @return an ECPrivateKey instance for signing or null if not available
190
*/
191
ECPrivateKey getPrivateKey();
192
193
/**
194
* Getter for the Id of the EC Private Key used for signing JWTs.
195
* @return the key id of the private key or null if not specified
196
*/
197
String getPrivateKeyId();
198
}
199
```
200
201
**Usage Examples:**
202
203
```java
204
import com.auth0.jwt.interfaces.ECDSAKeyProvider;
205
import com.auth0.jwt.algorithms.Algorithm;
206
import java.security.interfaces.ECPublicKey;
207
import java.security.interfaces.ECPrivateKey;
208
import java.util.Map;
209
import java.util.concurrent.ConcurrentHashMap;
210
211
// ECDSA key provider with thread-safe key storage
212
public class ConcurrentECDSAKeyProvider implements ECDSAKeyProvider {
213
private final Map<String, ECPublicKey> publicKeys;
214
private final ECPrivateKey privateKey;
215
private final String privateKeyId;
216
217
public ConcurrentECDSAKeyProvider(ECPrivateKey privateKey, String privateKeyId) {
218
this.privateKey = privateKey;
219
this.privateKeyId = privateKeyId;
220
this.publicKeys = new ConcurrentHashMap<>();
221
}
222
223
public void addPublicKey(String keyId, ECPublicKey publicKey) {
224
publicKeys.put(keyId, publicKey);
225
}
226
227
public void removePublicKey(String keyId) {
228
publicKeys.remove(keyId);
229
}
230
231
@Override
232
public ECPublicKey getPublicKeyById(String keyId) {
233
return publicKeys.get(keyId);
234
}
235
236
@Override
237
public ECPrivateKey getPrivateKey() {
238
return privateKey;
239
}
240
241
@Override
242
public String getPrivateKeyId() {
243
return privateKeyId;
244
}
245
}
246
247
// Usage with ECDSA algorithm
248
ECDSAKeyProvider keyProvider = new ConcurrentECDSAKeyProvider(ecPrivateKey, "ec-key-1");
249
keyProvider.addPublicKey("ec-key-1", ecPublicKey1);
250
keyProvider.addPublicKey("ec-key-2", ecPublicKey2);
251
252
Algorithm algorithm = Algorithm.ECDSA256(keyProvider);
253
254
// Create and verify JWT
255
String token = JWT.create()
256
.withKeyId("ec-key-1")
257
.withSubject("user123")
258
.sign(algorithm);
259
260
JWTVerifier verifier = JWT.require(algorithm).build();
261
DecodedJWT jwt = verifier.verify(token);
262
```
263
264
### Advanced Key Provider Patterns
265
266
Common patterns for implementing sophisticated key management scenarios.
267
268
**Key Rotation Support:**
269
270
```java
271
import java.time.Instant;
272
import java.util.concurrent.ConcurrentHashMap;
273
274
public class RotatingRSAKeyProvider implements RSAKeyProvider {
275
private final Map<String, KeyWithExpiry> publicKeys;
276
private volatile RSAPrivateKey currentPrivateKey;
277
private volatile String currentPrivateKeyId;
278
279
private static class KeyWithExpiry {
280
final RSAPublicKey key;
281
final Instant expiresAt;
282
283
KeyWithExpiry(RSAPublicKey key, Instant expiresAt) {
284
this.key = key;
285
this.expiresAt = expiresAt;
286
}
287
288
boolean isExpired() {
289
return Instant.now().isAfter(expiresAt);
290
}
291
}
292
293
public RotatingRSAKeyProvider() {
294
this.publicKeys = new ConcurrentHashMap<>();
295
}
296
297
public void addPublicKey(String keyId, RSAPublicKey key, Instant expiresAt) {
298
publicKeys.put(keyId, new KeyWithExpiry(key, expiresAt));
299
}
300
301
public void rotatePrivateKey(RSAPrivateKey newPrivateKey, String newKeyId) {
302
this.currentPrivateKey = newPrivateKey;
303
this.currentPrivateKeyId = newKeyId;
304
}
305
306
@Override
307
public RSAPublicKey getPublicKeyById(String keyId) {
308
KeyWithExpiry keyWithExpiry = publicKeys.get(keyId);
309
if (keyWithExpiry == null || keyWithExpiry.isExpired()) {
310
return null;
311
}
312
return keyWithExpiry.key;
313
}
314
315
@Override
316
public RSAPrivateKey getPrivateKey() {
317
return currentPrivateKey;
318
}
319
320
@Override
321
public String getPrivateKeyId() {
322
return currentPrivateKeyId;
323
}
324
325
// Cleanup expired keys
326
public void cleanupExpiredKeys() {
327
publicKeys.entrySet().removeIf(entry -> entry.getValue().isExpired());
328
}
329
}
330
```
331
332
**Remote Key Fetching:**
333
334
```java
335
import java.net.http.HttpClient;
336
import java.net.http.HttpRequest;
337
import java.net.http.HttpResponse;
338
import java.net.URI;
339
import java.security.spec.X509EncodedKeySpec;
340
import java.security.KeyFactory;
341
import java.util.Base64;
342
343
public class RemoteRSAKeyProvider implements RSAKeyProvider {
344
private final HttpClient httpClient;
345
private final String jwksUrl;
346
private final Map<String, RSAPublicKey> keyCache;
347
private final RSAPrivateKey privateKey;
348
private final String privateKeyId;
349
350
public RemoteRSAKeyProvider(String jwksUrl, RSAPrivateKey privateKey, String privateKeyId) {
351
this.httpClient = HttpClient.newHttpClient();
352
this.jwksUrl = jwksUrl;
353
this.keyCache = new ConcurrentHashMap<>();
354
this.privateKey = privateKey;
355
this.privateKeyId = privateKeyId;
356
}
357
358
@Override
359
public RSAPublicKey getPublicKeyById(String keyId) {
360
// Check cache first
361
RSAPublicKey cachedKey = keyCache.get(keyId);
362
if (cachedKey != null) {
363
return cachedKey;
364
}
365
366
// Fetch from remote JWKS endpoint
367
try {
368
RSAPublicKey fetchedKey = fetchPublicKeyFromJwks(keyId);
369
if (fetchedKey != null) {
370
keyCache.put(keyId, fetchedKey);
371
}
372
return fetchedKey;
373
} catch (Exception e) {
374
// Log error and return null
375
System.err.println("Failed to fetch public key " + keyId + ": " + e.getMessage());
376
return null;
377
}
378
}
379
380
private RSAPublicKey fetchPublicKeyFromJwks(String keyId) throws Exception {
381
HttpRequest request = HttpRequest.newBuilder()
382
.uri(URI.create(jwksUrl))
383
.GET()
384
.build();
385
386
HttpResponse<String> response = httpClient.send(request,
387
HttpResponse.BodyHandlers.ofString());
388
389
if (response.statusCode() != 200) {
390
throw new RuntimeException("Failed to fetch JWKS: " + response.statusCode());
391
}
392
393
// Parse JWKS response and extract public key for keyId
394
// This is a simplified example - real implementation would use JSON parsing
395
return parseJwksResponse(response.body(), keyId);
396
}
397
398
private RSAPublicKey parseJwksResponse(String jwksJson, String keyId) throws Exception {
399
// Simplified parsing - use a proper JSON library in production
400
// Extract the base64-encoded public key for the given keyId
401
// Convert to RSAPublicKey and return
402
403
// This is pseudo-code for demonstration
404
String publicKeyBase64 = extractPublicKeyFromJwks(jwksJson, keyId);
405
byte[] keyBytes = Base64.getDecoder().decode(publicKeyBase64);
406
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
407
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
408
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
409
}
410
411
private String extractPublicKeyFromJwks(String jwksJson, String keyId) {
412
// Implement JWKS parsing logic here
413
return ""; // Placeholder
414
}
415
416
@Override
417
public RSAPrivateKey getPrivateKey() {
418
return privateKey;
419
}
420
421
@Override
422
public String getPrivateKeyId() {
423
return privateKeyId;
424
}
425
}
426
```
427
428
**Multi-Tenant Key Provider:**
429
430
```java
431
public class MultiTenantRSAKeyProvider implements RSAKeyProvider {
432
private final Map<String, TenantKeySet> tenantKeys;
433
private final String currentTenant;
434
435
private static class TenantKeySet {
436
final Map<String, RSAPublicKey> publicKeys;
437
final RSAPrivateKey privateKey;
438
final String privateKeyId;
439
440
TenantKeySet(Map<String, RSAPublicKey> publicKeys,
441
RSAPrivateKey privateKey,
442
String privateKeyId) {
443
this.publicKeys = new HashMap<>(publicKeys);
444
this.privateKey = privateKey;
445
this.privateKeyId = privateKeyId;
446
}
447
}
448
449
public MultiTenantRSAKeyProvider(String currentTenant) {
450
this.tenantKeys = new ConcurrentHashMap<>();
451
this.currentTenant = currentTenant;
452
}
453
454
public void addTenant(String tenantId, Map<String, RSAPublicKey> publicKeys,
455
RSAPrivateKey privateKey, String privateKeyId) {
456
tenantKeys.put(tenantId, new TenantKeySet(publicKeys, privateKey, privateKeyId));
457
}
458
459
@Override
460
public RSAPublicKey getPublicKeyById(String keyId) {
461
TenantKeySet tenantKeySet = tenantKeys.get(currentTenant);
462
if (tenantKeySet == null) {
463
return null;
464
}
465
return tenantKeySet.publicKeys.get(keyId);
466
}
467
468
@Override
469
public RSAPrivateKey getPrivateKey() {
470
TenantKeySet tenantKeySet = tenantKeys.get(currentTenant);
471
return tenantKeySet != null ? tenantKeySet.privateKey : null;
472
}
473
474
@Override
475
public String getPrivateKeyId() {
476
TenantKeySet tenantKeySet = tenantKeys.get(currentTenant);
477
return tenantKeySet != null ? tenantKeySet.privateKeyId : null;
478
}
479
}
480
```
481
482
### JWTPartsParser Interface
483
484
Interface for parsing JWT header and payload JSON strings into structured objects.
485
486
```java { .api }
487
/**
488
* Parser interface for JWT parts providing structured access to header and payload data
489
*/
490
public interface JWTPartsParser {
491
/**
492
* Parse a payload JSON string into a Payload object.
493
* @param json the JSON string representing the payload
494
* @return a Payload instance providing access to payload claims
495
* @throws com.auth0.jwt.exceptions.JWTDecodeException if JSON parsing fails
496
*/
497
Payload parsePayload(String json) throws JWTDecodeException;
498
499
/**
500
* Parse a header JSON string into a Header object.
501
* @param json the JSON string representing the header
502
* @return a Header instance providing access to header claims
503
* @throws com.auth0.jwt.exceptions.JWTDecodeException if JSON parsing fails
504
*/
505
Header parseHeader(String json) throws JWTDecodeException;
506
}
507
```
508
509
**Usage Examples:**
510
511
```java
512
import com.auth0.jwt.interfaces.JWTPartsParser;
513
import com.auth0.jwt.interfaces.Payload;
514
import com.auth0.jwt.interfaces.Header;
515
import com.auth0.jwt.impl.JWTParser;
516
517
// Use the default parser implementation
518
JWTPartsParser parser = new JWTParser();
519
520
// Parse header JSON
521
String headerJson = "{\"alg\":\"HS256\",\"typ\":\"JWT\",\"kid\":\"key-1\"}";
522
try {
523
Header header = parser.parseHeader(headerJson);
524
String algorithm = header.getAlgorithm(); // "HS256"
525
String keyId = header.getKeyId(); // "key-1"
526
System.out.println("Parsed algorithm: " + algorithm);
527
} catch (JWTDecodeException e) {
528
System.err.println("Failed to parse header: " + e.getMessage());
529
}
530
531
// Parse payload JSON
532
String payloadJson = "{\"sub\":\"user123\",\"iss\":\"auth0\",\"exp\":1234567890}";
533
try {
534
Payload payload = parser.parsePayload(payloadJson);
535
String subject = payload.getSubject(); // "user123"
536
String issuer = payload.getIssuer(); // "auth0"
537
System.out.println("Parsed subject: " + subject);
538
} catch (JWTDecodeException e) {
539
System.err.println("Failed to parse payload: " + e.getMessage());
540
}
541
```
542
543
## Best Practices
544
545
### Key Security
546
- **Private Key Protection**: Store private keys securely using hardware security modules (HSMs) or secure key management services
547
- **Key Rotation**: Implement regular key rotation to limit exposure window
548
- **Access Control**: Restrict access to private keys to authorized processes only
549
550
### Performance Optimization
551
- **Key Caching**: Cache frequently accessed public keys to avoid repeated network calls
552
- **Connection Pooling**: Use connection pooling for remote key fetching
553
- **Async Loading**: Consider asynchronous key loading for high-throughput scenarios
554
555
### Error Handling
556
- **Graceful Degradation**: Handle key fetch failures gracefully with appropriate fallbacks
557
- **Logging**: Log key resolution failures for monitoring and debugging
558
- **Timeouts**: Implement timeouts for remote key fetching to prevent blocking
559
560
### Multi-Tenant Considerations
561
- **Tenant Isolation**: Ensure complete isolation of keys between tenants
562
- **Resource Limits**: Implement limits on cached keys per tenant
563
- **Audit Logging**: Log key access for security auditing
564
565
## Types
566
567
```java { .api }
568
/**
569
* Generic key provider interface for dynamic key resolution
570
*/
571
public interface KeyProvider<U, R> {
572
U getPublicKeyById(String keyId);
573
R getPrivateKey();
574
String getPrivateKeyId();
575
}
576
577
/**
578
* RSA-specific key provider interface
579
*/
580
public interface RSAKeyProvider extends KeyProvider<RSAPublicKey, RSAPrivateKey> {
581
RSAPublicKey getPublicKeyById(String keyId);
582
RSAPrivateKey getPrivateKey();
583
String getPrivateKeyId();
584
}
585
586
/**
587
* ECDSA-specific key provider interface
588
*/
589
public interface ECDSAKeyProvider extends KeyProvider<ECPublicKey, ECPrivateKey> {
590
ECPublicKey getPublicKeyById(String keyId);
591
ECPrivateKey getPrivateKey();
592
String getPrivateKeyId();
593
}
594
595
/**
596
* JWT parts parser interface
597
*/
598
public interface JWTPartsParser {
599
Payload parsePayload(String json) throws JWTDecodeException;
600
Header parseHeader(String json) throws JWTDecodeException;
601
}
602
```