Nimbus JOSE + JWT

JavaJWTJOSEAuthentication LibraryEncryptionSignatureJSON Web TokenSecurity

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

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());
    }
}