or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

algorithms.mdclaims.mdindex.mdjwt-creation.mdjwt-decoding.mdjwt-verification.mdkey-providers.md

key-providers.mddocs/

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

```