Nimbus JOSE + JWT
Authentication Library
Nimbus JOSE + JWT
Overview
Nimbus JOSE + JWT is the most popular and robust JWT (JSON Web Token) library for Java that fully implements the JOSE (JavaScript Object Signing and Encryption) standards and covers all standard signature (JWS) and encryption (JWE) algorithms.
Details
Nimbus JOSE + JWT is a comprehensive JWT implementation library for Java developed by Connect2id. Positioned as "the most popular and robust JSON Web Token (JWT) library for Java," it fully implements the JOSE (JavaScript Object Signing and Encryption) and JWT (JSON Web Token) standards.
This library provides a comprehensive API for signing and encrypting self-contained tokens such as OAuth 2.0 access tokens and OpenID Connect ID tokens. It works with Java 7+ and is designed with minimal dependencies, making it easy to integrate into existing Java projects.
For signature capabilities, it supports all standard JWS signature algorithms including RS256, RS384, RS512 (RSA), ES256, ES384, ES512 (ECDSA), HS256, HS384, HS512 (HMAC), PS256, PS384, PS512 (RSA-PSS), and EdDSA (Ed25519). For encryption capabilities, it supports key encryption algorithms like RSA-OAEP, AES Key Wrap, ECDH-ES, PBES2, and content encryption algorithms like AES-GCM and AES-CBC+HMAC-SHA2.
With full JSON Web Key (JWK) support, it enables generation, parsing, and serialization of RSA, EC (elliptic curve), AES, and HMAC keys. It also provides advanced features such as JWK Set operations, key rotation, and key expiration management.
It has extensive usage in Spring Security, OAuth 2.0 libraries, and microservice architectures, meeting enterprise-grade security requirements. Performance optimization ensures stable operation even in high-load environments, and detailed error handling and logging capabilities make problem identification and resolution easier.
Pros and Cons
Pros
- Complete JOSE/JWT Implementation: Supports all standard signature and encryption algorithms
- High Stability: Over 10 years of development experience and extensive production usage
- Minimal Dependencies: Works with Java 7+ and minimizes external dependencies
- Comprehensive API: Supports complete lifecycle from token generation to verification
- Full JWK Support: JSON Web Key generation, management, and rotation capabilities
- Enterprise Ready: Excellent integration with Spring Security and OAuth 2.0
- High Performance: Fast processing through optimized algorithm implementation
- Detailed Documentation: Rich official documentation and sample code
Cons
- Java Only: Cannot be used in languages other than Java
- High Learning Curve: Requires understanding of JOSE/JWT specifications
- Configuration Complexity: Security configuration can become complex
- Library Size: Large library size due to rich feature set
- Memory Usage: Encryption processing may increase memory consumption
- Debugging Difficulty: Debugging encryption errors can be challenging
- Version Compatibility: Version management complexity due to JOSE specification changes
Key Links
- Nimbus JOSE + JWT Official Site
- GitHub Repository
- JavaDoc API Documentation
- Maven Repository
- Sample Code Collection
- JOSE & JWT Standard Specifications
- Connect2id Support
Code Examples
Adding Maven Dependency
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>10.3</version>
</dependency>
Basic JWT Creation and Verification
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jwt.*;
import java.util.Date;
public class JWTExample {
public static void main(String[] args) throws Exception {
// Create JWT using HMAC algorithm
createAndVerifyHMACJWT();
// Create JWT using RSA algorithm
createAndVerifyRSAJWT();
}
// JWT with HMAC signature
public static void createAndVerifyHMACJWT() throws Exception {
// Shared secret key
String sharedSecret = "mySecretKey123456789012345678901234567890";
// Create JWT claims set
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.subject("user123")
.issuer("https://example.com")
.audience("https://api.example.com")
.expirationTime(new Date(System.currentTimeMillis() + 60 * 60 * 1000)) // 1 hour later
.notBeforeTime(new Date())
.issueTime(new Date())
.jwtID("jwt-id-123")
.claim("role", "admin")
.claim("email", "[email protected]")
.build();
// Create JWS header
JWSHeader header = new JWSHeader(JWSAlgorithm.HS256);
// Create SignedJWT object
SignedJWT signedJWT = new SignedJWT(header, claimsSet);
// Apply HMAC signature
JWSSigner signer = new MACSigner(sharedSecret.getBytes());
signedJWT.sign(signer);
// Get JWT token
String jwtToken = signedJWT.serialize();
System.out.println("JWT Token: " + jwtToken);
// JWT verification
SignedJWT parsedJWT = SignedJWT.parse(jwtToken);
JWSVerifier verifier = new MACVerifier(sharedSecret.getBytes());
if (parsedJWT.verify(verifier)) {
System.out.println("JWT signature verified successfully");
// Get claims
JWTClaimsSet claims = parsedJWT.getJWTClaimsSet();
System.out.println("Subject: " + claims.getSubject());
System.out.println("Role: " + claims.getStringClaim("role"));
System.out.println("Email: " + claims.getStringClaim("email"));
// Expiration check
if (claims.getExpirationTime().after(new Date())) {
System.out.println("Token is valid");
} else {
System.out.println("Token has expired");
}
} else {
System.out.println("JWT signature verification failed");
}
}
}
JWT with RSA Key Pair
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
public static void createAndVerifyRSAJWT() throws Exception {
// Generate RSA key pair
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
keyGenerator.initialize(2048);
KeyPair keyPair = keyGenerator.generateKeyPair();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// Create JWT claims set
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.subject("user456")
.issuer("https://secure.example.com")
.audience("https://api.example.com")
.expirationTime(new Date(System.currentTimeMillis() + 30 * 60 * 1000)) // 30 minutes later
.issueTime(new Date())
.claim("scope", "read write")
.claim("department", "engineering")
.build();
// Create JWS header (RS256 algorithm)
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256)
.keyID("key-1")
.type(JOSEObjectType.JWT)
.build();
// Create SignedJWT object
SignedJWT signedJWT = new SignedJWT(header, claimsSet);
// Apply RSA signature
JWSSigner signer = new RSASSASigner(privateKey);
signedJWT.sign(signer);
String jwtToken = signedJWT.serialize();
System.out.println("RSA JWT Token: " + jwtToken);
// JWT verification
SignedJWT parsedJWT = SignedJWT.parse(jwtToken);
JWSVerifier verifier = new RSASSAVerifier(publicKey);
if (parsedJWT.verify(verifier)) {
System.out.println("RSA JWT signature verified successfully");
JWTClaimsSet claims = parsedJWT.getJWTClaimsSet();
System.out.println("Subject: " + claims.getSubject());
System.out.println("Scope: " + claims.getStringClaim("scope"));
System.out.println("Department: " + claims.getStringClaim("department"));
}
}
Encrypted JWT using JWE
import com.nimbusds.jose.crypto.AESEncrypter;
import com.nimbusds.jose.crypto.AESDecrypter;
public static void createAndDecryptJWE() throws Exception {
// Encryption key (256 bits)
byte[] secretKey = new byte[32]; // 256 bits
new SecureRandom().nextBytes(secretKey);
// Create JWT claims set
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.subject("confidential-user")
.issuer("https://secure.example.com")
.claim("sensitive-data", "top-secret-information")
.claim("security-level", "confidential")
.expirationTime(new Date(System.currentTimeMillis() + 15 * 60 * 1000)) // 15 minutes later
.build();
// Create JWE header
JWEHeader header = new JWEHeader.Builder(JWEAlgorithm.DIR, EncryptionMethod.A256GCM)
.contentType("JWT")
.build();
// Create EncryptedJWT object
EncryptedJWT encryptedJWT = new EncryptedJWT(header, claimsSet);
// Apply AES encryption
JWEEncrypter encrypter = new AESEncrypter(secretKey);
encryptedJWT.encrypt(encrypter);
String jweToken = encryptedJWT.serialize();
System.out.println("JWE Token: " + jweToken);
// JWE decryption
EncryptedJWT parsedJWE = EncryptedJWT.parse(jweToken);
JWEDecrypter decrypter = new AESDecrypter(secretKey);
parsedJWE.decrypt(decrypter);
JWTClaimsSet decryptedClaims = parsedJWE.getJWTClaimsSet();
System.out.println("Decrypted Subject: " + decryptedClaims.getSubject());
System.out.println("Sensitive Data: " + decryptedClaims.getStringClaim("sensitive-data"));
}
JSON Web Key (JWK) Generation and Usage
import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.*;
public static void createAndUseJWK() throws Exception {
// Generate RSA JWK
RSAKey rsaKey = new RSAKeyGenerator(2048)
.keyID("rsa-key-1")
.keyUse(KeyUse.SIGNATURE)
.algorithm(JWSAlgorithm.RS256)
.generate();
System.out.println("RSA JWK: " + rsaKey.toJSONString());
// Generate EC JWK
ECKey ecKey = new ECKeyGenerator(Curve.P_256)
.keyID("ec-key-1")
.keyUse(KeyUse.SIGNATURE)
.algorithm(JWSAlgorithm.ES256)
.generate();
System.out.println("EC JWK: " + ecKey.toJSONString());
// Create JWK Set
JWKSet jwkSet = new JWKSet(Arrays.asList(rsaKey, ecKey));
System.out.println("JWK Set: " + jwkSet.toJSONObject());
// JWT signing using JWK
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.subject("jwk-user")
.issuer("https://jwk.example.com")
.expirationTime(new Date(System.currentTimeMillis() + 60 * 60 * 1000))
.build();
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256)
.keyID(rsaKey.getKeyID())
.build();
SignedJWT signedJWT = new SignedJWT(header, claimsSet);
// Sign with RSA private key
JWSSigner signer = new RSASSASigner(rsaKey);
signedJWT.sign(signer);
System.out.println("JWK signed JWT: " + signedJWT.serialize());
// Verify with public key
JWSVerifier verifier = new RSASSAVerifier(rsaKey.toRSAPublicKey());
System.out.println("JWK verification: " + signedJWT.verify(verifier));
}
Custom Claims and Validation
import com.nimbusds.jwt.proc.*;
import com.nimbusds.jose.proc.*;
public static void customClaimsValidation() throws Exception {
String sharedSecret = "mySecretKey123456789012345678901234567890";
// Create JWT with custom claims
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.subject("custom-user")
.issuer("https://custom.example.com")
.audience("https://api.example.com")
.expirationTime(new Date(System.currentTimeMillis() + 60 * 60 * 1000))
.issueTime(new Date())
.claim("role", "premium-user")
.claim("permissions", Arrays.asList("read", "write", "delete"))
.claim("subscription", "premium")
.claim("user-level", 5)
.build();
JWSHeader header = new JWSHeader(JWSAlgorithm.HS256);
SignedJWT signedJWT = new SignedJWT(header, claimsSet);
JWSSigner signer = new MACSigner(sharedSecret.getBytes());
signedJWT.sign(signer);
String token = signedJWT.serialize();
// Configure JWT processor
ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
// Set JWS key source
JWKSource<SecurityContext> keySource = new ImmutableSecret<>(sharedSecret.getBytes());
jwtProcessor.setJWSKeySource(keySource);
// Set custom claims verifier
jwtProcessor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier<>(
new JWTClaimsSet.Builder()
.issuer("https://custom.example.com")
.audience("https://api.example.com")
.build(),
new HashSet<>(Arrays.asList("sub", "iat", "exp", "role"))
));
try {
// Process and verify JWT
JWTClaimsSet processedClaims = jwtProcessor.process(token, null);
System.out.println("JWT validated successfully");
System.out.println("Role: " + processedClaims.getStringClaim("role"));
System.out.println("Permissions: " + processedClaims.getStringListClaim("permissions"));
System.out.println("User Level: " + processedClaims.getIntegerClaim("user-level"));
// Custom business logic validation
String role = processedClaims.getStringClaim("role");
if ("premium-user".equals(role)) {
System.out.println("Premium user access granted");
} else {
System.out.println("Standard user access");
}
} catch (BadJWTException e) {
System.err.println("JWT validation failed: " + e.getMessage());
}
}
JWK Set Loading and Key Rotation
import java.net.URL;
public static void jwkSetFromURL() throws Exception {
// Load JWK Set from URL (JWKS endpoint)
URL jwkSetURL = new URL("https://example.com/.well-known/jwks.json");
JWKSet jwkSet = JWKSet.load(jwkSetURL);
System.out.println("Loaded JWK Set with " + jwkSet.getKeys().size() + " keys");
// Search for key by specific key ID
JWK key = jwkSet.getKeyByKeyId("key-1");
if (key != null) {
System.out.println("Found key: " + key.getKeyID());
// Get RSA public key
if (key instanceof RSAKey) {
RSAKey rsaKey = (RSAKey) key;
RSAPublicKey publicKey = rsaKey.toRSAPublicKey();
// Create verifier
JWSVerifier verifier = new RSASSAVerifier(publicKey);
System.out.println("Verifier created for key: " + key.getKeyID());
}
}
// Multiple key management for key rotation
for (JWK jwk : jwkSet.getKeys()) {
System.out.println("Key ID: " + jwk.getKeyID() +
", Algorithm: " + jwk.getAlgorithm() +
", Use: " + jwk.getKeyUse());
}
}
Error Handling and Logging
import java.text.ParseException;
public static void errorHandlingExample() {
try {
// Parse invalid JWT token
String invalidToken = "invalid.jwt.token";
SignedJWT.parse(invalidToken);
} catch (ParseException e) {
System.err.println("JWT parsing failed: " + e.getMessage());
}
try {
// Simulate expired token
JWTClaimsSet expiredClaims = new JWTClaimsSet.Builder()
.subject("expired-user")
.expirationTime(new Date(System.currentTimeMillis() - 60 * 60 * 1000)) // 1 hour ago
.build();
// Expiration check
Date now = new Date();
if (expiredClaims.getExpirationTime().before(now)) {
throw new RuntimeException("Token has expired");
}
} catch (Exception e) {
System.err.println("Token validation error: " + e.getMessage());
}
// Comprehensive error handling with JWT processor
try {
ConfigurableJWTProcessor<SecurityContext> processor = new DefaultJWTProcessor<>();
// Processing configuration...
} catch (BadJOSEException e) {
System.err.println("JOSE processing error: " + e.getMessage());
} catch (JOSEException e) {
System.err.println("JOSE operation error: " + e.getMessage());
}
}