CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-apereo-cas--cas-server-core-util-api

Apereo CAS Core Utilities - A comprehensive utility library providing functional programming constructs, encryption utilities, configuration helpers, and core infrastructure components for the Central Authentication Service framework

Pending
Overview
Eval results
Files

jwt-utilities.mddocs/

JWT Utilities

JSON Web Token signing and encryption utilities with support for various algorithms, key management strategies, and integration with CAS authentication flows.

JsonWebTokenSigner

JWT signing utilities for creating and validating signed tokens with configurable algorithms and key management.

public class JsonWebTokenSigner {
    
    // Supported algorithms (excludes 'none' for security)
    public static final Set<String> ALGORITHM_ALL_EXCEPT_NONE = Set.of(
        "HS256", "HS384", "HS512",           // HMAC algorithms
        "RS256", "RS384", "RS512",           // RSA algorithms  
        "ES256", "ES384", "ES512",           // ECDSA algorithms
        "PS256", "PS384", "PS512"            // RSA-PSS algorithms
    );
    
    // Constructors
    public JsonWebTokenSigner(Key signingKey);
    public JsonWebTokenSigner(Key signingKey, String algorithm);
    public JsonWebTokenSigner(String secretKey, String algorithm);
    
    // Signing operations
    public byte[] sign(byte[] value);
    public String sign(JwtClaims claims);
    public String sign(JwtClaims claims, Map<String, Object> headers);
    
    // Validation operations
    public boolean verify(String token);
    public JwtClaims parse(String token);
    public JwtClaims parseAndVerify(String token);
    
    // Configuration
    public String getAlgorithm();
    public Key getSigningKey();
}

Usage Examples

Basic JWT signing:

@Service
public class TokenService {
    
    private final JsonWebTokenSigner jwtSigner;
    
    public TokenService(@Value("${cas.jwt.signing-secret}") String secret) {
        // HMAC-based signer with HS256
        this.jwtSigner = new JsonWebTokenSigner(secret, "HS256");
    }
    
    public String createAccessToken(String userId, List<String> roles) {
        // Create JWT claims
        JwtClaims claims = new JwtClaims();
        claims.setSubject(userId);
        claims.setIssuer("cas-server");
        claims.setAudience("cas-clients");
        claims.setExpirationTimeMinutesInTheFuture(60); // 1 hour
        claims.setIssuedAtToNow();
        claims.setNotBeforeMinutesInThePast(1);
        claims.setJwtId(UUID.randomUUID().toString());
        
        // Add custom claims
        claims.setClaim("roles", roles);
        claims.setClaim("tokenType", "access");
        
        // Sign and return token
        return jwtSigner.sign(claims);
    }
    
    public String createRefreshToken(String userId) {
        JwtClaims claims = new JwtClaims();
        claims.setSubject(userId);
        claims.setIssuer("cas-server");
        claims.setExpirationTimeMinutesInTheFuture(10080); // 7 days
        claims.setIssuedAtToNow();
        claims.setClaim("tokenType", "refresh");
        
        return jwtSigner.sign(claims);
    }
    
    public boolean validateToken(String token) {
        try {
            return jwtSigner.verify(token);
        } catch (Exception e) {
            log.warn("Token validation failed", e);
            return false;
        }
    }
    
    public Optional<TokenInfo> parseToken(String token) {
        try {
            JwtClaims claims = jwtSigner.parseAndVerify(token);
            
            TokenInfo info = new TokenInfo();
            info.setUserId(claims.getSubject());
            info.setRoles((List<String>) claims.getClaimValue("roles"));
            info.setTokenType((String) claims.getClaimValue("tokenType"));
            info.setExpiresAt(claims.getExpirationTime().getValueInMillis());
            
            return Optional.of(info);
            
        } catch (Exception e) {
            log.error("Failed to parse token", e);
            return Optional.empty();
        }
    }
}

RSA-based JWT signing:

@Configuration
public class JwtConfiguration {
    
    @Bean
    public JsonWebTokenSigner rsaJwtSigner() throws Exception {
        // Load RSA private key for signing
        PrivateKey privateKey = loadPrivateKey("classpath:jwt-private.pem");
        return new JsonWebTokenSigner(privateKey, "RS256");
    }
    
    @Bean
    public JsonWebTokenValidator rsaJwtValidator() throws Exception {
        // Load RSA public key for validation
        PublicKey publicKey = loadPublicKey("classpath:jwt-public.pem");
        return new JsonWebTokenValidator(publicKey, "RS256");
    }
    
    private PrivateKey loadPrivateKey(String location) throws Exception {
        Resource resource = resourceLoader.getResource(location);
        return AbstractCipherExecutor.extractPrivateKeyFromResource(resource.getURI().toString());
    }
    
    private PublicKey loadPublicKey(String location) throws Exception {
        Resource resource = resourceLoader.getResource(location);
        return AbstractCipherExecutor.extractPublicKeyFromResource(resource.getURI().toString());
    }
}

Custom headers and claims:

@Service
public class AdvancedJwtService {
    
    private final JsonWebTokenSigner jwtSigner;
    
    public String createTokenWithCustomHeaders(String userId, String clientId) {
        // Create claims
        JwtClaims claims = new JwtClaims();
        claims.setSubject(userId);
        claims.setAudience(clientId);
        claims.setExpirationTimeMinutesInTheFuture(30);
        claims.setIssuedAtToNow();
        
        // Custom claims for CAS context
        claims.setClaim("authenticationMethod", "password");
        claims.setClaim("serviceTicket", generateServiceTicket());
        claims.setClaim("principalAttributes", getUserAttributes(userId));
        
        // Custom headers
        Map<String, Object> headers = new HashMap<>();
        headers.put("typ", "JWT");
        headers.put("alg", jwtSigner.getAlgorithm());
        headers.put("kid", "cas-2024-01"); // Key ID for rotation
        headers.put("cas-version", "7.2.4");
        
        return jwtSigner.sign(claims, headers);
    }
    
    public String createServiceToken(String service, Map<String, Object> attributes) {
        JwtClaims claims = new JwtClaims();
        claims.setAudience(service);
        claims.setIssuer("https://cas.example.com");
        claims.setExpirationTimeMinutesInTheFuture(5); // Short-lived for services
        claims.setIssuedAtToNow();
        
        // Service-specific claims
        claims.setClaim("serviceAttributes", attributes);
        claims.setClaim("grantType", "service_ticket");
        
        Map<String, Object> headers = Map.of(
            "typ", "service+jwt",
            "purpose", "service-validation"
        );
        
        return jwtSigner.sign(claims, headers);
    }
}

JsonWebTokenEncryptor

JWT encryption utilities for creating encrypted tokens with various key management strategies.

public class JsonWebTokenEncryptor {
    
    // Supported encryption algorithms (excludes 'none' for security)  
    public static final List<String> ALGORITHM_ALL_EXCEPT_NONE = List.of(
        "RSA1_5", "RSA-OAEP", "RSA-OAEP-256",    // RSA key encryption
        "A128KW", "A192KW", "A256KW",             // AES key wrapping
        "dir",                                     // Direct encryption
        "A128GCMKW", "A192GCMKW", "A256GCMKW"     // AES-GCM key wrapping
    );
    
    // Constructors
    public JsonWebTokenEncryptor(Key encryptionKey);
    public JsonWebTokenEncryptor(Key encryptionKey, String keyAlgorithm, String contentAlgorithm);
    public JsonWebTokenEncryptor(String secretKey, String keyAlgorithm, String contentAlgorithm);
    
    // Encryption operations
    public String encrypt(Serializable payload);
    public String encrypt(Object payload, Map<String, Object> headers);
    public String encrypt(String plaintext);
    
    // Decryption operations  
    public String decrypt(String encryptedToken);
    public <T> T decrypt(String encryptedToken, Class<T> type);
    public Map<String, Object> decryptToMap(String encryptedToken);
    
    // Configuration
    public String getKeyAlgorithm();
    public String getContentAlgorithm();
    public Key getEncryptionKey();
}

Usage Examples

Basic JWT encryption:

@Service  
public class SecureTokenService {
    
    private final JsonWebTokenEncryptor jwtEncryptor;
    private final JsonWebTokenSigner jwtSigner;
    
    public SecureTokenService(
            @Value("${cas.jwt.encryption-secret}") String encryptionSecret,
            @Value("${cas.jwt.signing-secret}") String signingSecret) {
        
        // AES-GCM encryption
        this.jwtEncryptor = new JsonWebTokenEncryptor(
            encryptionSecret, 
            "dir",           // Direct key agreement
            "A256GCM"        // AES-256-GCM content encryption
        );
        
        // HMAC signing
        this.jwtSigner = new JsonWebTokenSigner(signingSecret, "HS256");
    }
    
    public String createSecureToken(SensitiveData data) {
        // First encrypt the sensitive data
        String encryptedData = jwtEncryptor.encrypt(data);
        
        // Then sign the encrypted token
        JwtClaims claims = new JwtClaims();
        claims.setSubject(data.getUserId());
        claims.setExpirationTimeMinutesInTheFuture(15);
        claims.setIssuedAtToNow();
        claims.setClaim("encryptedPayload", encryptedData);
        
        return jwtSigner.sign(claims);
    }
    
    public Optional<SensitiveData> extractSecureData(String token) {
        try {
            // Verify and parse the signed token
            JwtClaims claims = jwtSigner.parseAndVerify(token);
            String encryptedPayload = (String) claims.getClaimValue("encryptedPayload");
            
            // Decrypt the payload
            SensitiveData data = jwtEncryptor.decrypt(encryptedPayload, SensitiveData.class);
            return Optional.of(data);
            
        } catch (Exception e) {
            log.error("Failed to extract secure data from token", e);
            return Optional.empty();
        }
    }
}

RSA-based encryption with key rotation:

@Service
public class KeyRotationAwareJwtService {
    
    private final Map<String, JsonWebTokenEncryptor> encryptors;
    private final Map<String, JsonWebTokenSigner> signers;
    private String currentKeyId;
    
    public KeyRotationAwareJwtService() {
        this.encryptors = new ConcurrentHashMap<>();
        this.signers = new ConcurrentHashMap<>();
        initializeKeys();
    }
    
    private void initializeKeys() {
        try {
            // Load current keys
            String keyId = "2024-01";
            PublicKey publicKey = loadPublicKey("jwt-public-" + keyId + ".pem");
            PrivateKey privateKey = loadPrivateKey("jwt-private-" + keyId + ".pem");
            
            // RSA encryption (recipient uses private key to decrypt)
            encryptors.put(keyId, new JsonWebTokenEncryptor(publicKey, "RSA-OAEP", "A256GCM"));
            
            // RSA signing (sender uses private key to sign)  
            signers.put(keyId, new JsonWebTokenSigner(privateKey, "RS256"));
            
            this.currentKeyId = keyId;
            
        } catch (Exception e) {
            throw new RuntimeException("Failed to initialize JWT keys", e);
        }
    }
    
    public String createRotationAwareToken(Object payload) {
        // Encrypt with current public key
        JsonWebTokenEncryptor encryptor = encryptors.get(currentKeyId);
        
        Map<String, Object> headers = Map.of(
            "kid", currentKeyId,  // Key ID for rotation
            "alg", "RSA-OAEP",
            "enc", "A256GCM"
        );
        
        String encryptedToken = encryptor.encrypt(payload, headers);
        
        // Sign with current private key
        JwtClaims wrapperClaims = new JwtClaims();
        wrapperClaims.setIssuedAtToNow();
        wrapperClaims.setExpirationTimeMinutesInTheFuture(60);
        wrapperClaims.setClaim("encryptedJwt", encryptedToken);
        
        JsonWebTokenSigner signer = signers.get(currentKeyId);
        Map<String, Object> signHeaders = Map.of("kid", currentKeyId);
        
        return signer.sign(wrapperClaims, signHeaders);
    }
    
    public <T> Optional<T> extractPayload(String token, Class<T> type) {
        try {
            // Parse outer signed JWT to get key ID
            String[] parts = token.split("\\.");
            String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]));
            JsonNode headerNode = objectMapper.readTree(headerJson);
            String keyId = headerNode.get("kid").asText();
            
            // Verify signature with appropriate key
            JsonWebTokenSigner signer = signers.get(keyId);
            if (signer == null) {
                log.error("Unknown key ID: {}", keyId);
                return Optional.empty();
            }
            
            JwtClaims claims = signer.parseAndVerify(token);
            String encryptedJwt = (String) claims.getClaimValue("encryptedJwt");
            
            // Decrypt with appropriate key (need corresponding private key)
            JsonWebTokenEncryptor encryptor = getDecryptorForKeyId(keyId);
            T payload = encryptor.decrypt(encryptedJwt, type);
            
            return Optional.of(payload);
            
        } catch (Exception e) {
            log.error("Failed to extract payload from rotation-aware token", e);
            return Optional.empty();
        }
    }
}

Integration with CAS Authentication

CAS Service Token Creation

@Component
public class CasJwtTokenService {
    
    private final JsonWebTokenSigner signer;
    private final JsonWebTokenEncryptor encryptor;
    
    public String createServiceTicketJwt(Authentication authentication, Service service) {
        // Create CAS-specific claims
        JwtClaims claims = new JwtClaims();
        claims.setSubject(authentication.getPrincipal().getId());
        claims.setIssuer("https://cas.example.com");
        claims.setAudience(service.getId());
        claims.setExpirationTimeMinutesInTheFuture(5); // Short-lived service tickets
        claims.setIssuedAtToNow();
        claims.setJwtId(generateServiceTicketId());
        
        // CAS authentication context
        claims.setClaim("authenticationMethod", getAuthenticationMethod(authentication));
        claims.setClaim("authenticationTime", authentication.getAuthenticationDate().toEpochMilli());
        claims.setClaim("principalAttributes", authentication.getPrincipal().getAttributes());
        claims.setClaim("serviceId", service.getId());
        
        // Service-specific attributes
        if (service instanceof RegisteredService registeredService) {
            claims.setClaim("serviceName", registeredService.getName());
            claims.setClaim("serviceDescription", registeredService.getDescription());
        }
        
        return signer.sign(claims);
    }
    
    public String createTicketGrantingTicketJwt(Authentication authentication) {
        JwtClaims claims = new JwtClaims();
        claims.setSubject(authentication.getPrincipal().getId());
        claims.setIssuer("https://cas.example.com");
        claims.setExpirationTimeMinutesInTheFuture(480); // 8 hours
        claims.setIssuedAtToNow();
        claims.setJwtId(generateTicketGrantingTicketId());
        
        // TGT-specific claims
        claims.setClaim("ticketType", "TicketGrantingTicket");
        claims.setClaim("authenticationTime", authentication.getAuthenticationDate().toEpochMilli());
        claims.setClaim("principalId", authentication.getPrincipal().getId());
        claims.setClaim("credentialType", getCredentialType(authentication));
        
        // Encrypt TGT for additional security
        String signedToken = signer.sign(claims);
        return encryptor.encrypt(signedToken);
    }
    
    public boolean validateServiceTicketJwt(String token, Service service) {
        try {
            JwtClaims claims = signer.parseAndVerify(token);
            
            // Validate audience matches service
            if (!service.getId().equals(claims.getAudience().get(0))) {
                return false;
            }
            
            // Check expiration
            if (claims.getExpirationTime().isBefore(NumericDate.now())) {
                return false;
            }
            
            return true;
            
        } catch (Exception e) {
            log.error("Service ticket JWT validation failed", e);
            return false;
        }
    }
}

JWT-based Authentication Provider

@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {
    
    private final JsonWebTokenSigner jwtSigner;
    private final PrincipalResolver principalResolver;
    
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        
        if (!(authentication instanceof JwtAuthenticationToken jwtAuth)) {
            return null;
        }
        
        try {
            String token = jwtAuth.getToken();
            JwtClaims claims = jwtSigner.parseAndVerify(token);
            
            // Extract principal information
            String principalId = claims.getSubject();
            Map<String, Object> attributes = (Map<String, Object>) claims.getClaimValue("principalAttributes");
            
            // Create CAS principal
            Principal principal = principalResolver.resolve(
                new UsernamePasswordCredential(principalId, ""),
                Optional.empty(),
                Optional.empty(),
                Optional.empty()
            );
            
            // Create authentication result
            AuthenticationHandlerExecutionResult result = new DefaultAuthenticationHandlerExecutionResult(
                this,
                new BasicCredentialMetaData(new UsernamePasswordCredential(principalId, "")),
                principal
            );
            
            return DefaultAuthenticationBuilder.newInstance()
                .addCredential(new UsernamePasswordCredential(principalId, ""))
                .addSuccess("jwtAuthenticationHandler", result)
                .build();
                
        } catch (Exception e) {
            throw new AuthenticationException("JWT authentication failed", e);
        }
    }
    
    @Override
    public boolean supports(Class<?> authentication) {
        return JwtAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

This JWT utilities library provides comprehensive support for secure token-based authentication in CAS environments, with proper key management, algorithm selection, and integration with CAS authentication flows.

Install with Tessl CLI

npx tessl i tessl/maven-org-apereo-cas--cas-server-core-util-api

docs

core-utilities.md

cryptography.md

functional-programming.md

generators.md

http-clients.md

index.md

jwt-utilities.md

serialization.md

specialized-utilities.md

spring-integration.md

text-processing.md

tile.json