CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-jsonwebtoken--jjwt-impl

JJWT Implementation module providing concrete implementations of JSON Web Token (JWT) creation, parsing, verification, and cryptographic operations for Java and Android applications.

Pending
Overview
Eval results
Files

compression.mddocs/

Compression

The Compression functionality in JJWT Implementation provides payload compression and decompression capabilities for JWT tokens. This includes RFC 1951 DEFLATE compression (standard) and GZIP compression (common extension), helping reduce token size for large payloads while maintaining security and performance.

Core Compression Classes

Compression Algorithm Registry

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.CompressionAlgorithm;
import io.jsonwebtoken.impl.compression.DeflateCompressionAlgorithm;
import io.jsonwebtoken.impl.compression.GzipCompressionAlgorithm;
import io.jsonwebtoken.impl.io.StandardCompressionAlgorithms;

// Access compression algorithm registry
CompressionAlgorithm deflate = Jwts.ZIP.DEF;   // DEFLATE (RFC 1951)
CompressionAlgorithm gzip = Jwts.ZIP.GZIP;     // GZIP (non-standard but common)

// Get algorithm by ID
CompressionAlgorithm algorithm = Jwts.ZIP.get("DEF");

// List all available algorithms
Collection<CompressionAlgorithm> allAlgorithms = Jwts.ZIP.values();
Set<String> algorithmIds = Jwts.ZIP.get().keySet();

DEFLATE Compression

Basic DEFLATE Usage

import javax.crypto.SecretKey;

// Create compressed JWT with DEFLATE
SecretKey key = Jwts.SIG.HS256.key().build();

String compressedJWT = Jwts.builder()
    .subject("user-with-large-claims")
    .claim("permissions", Arrays.asList(
        "read:users", "write:users", "delete:users",
        "read:documents", "write:documents", "delete:documents",
        "read:reports", "write:reports", "admin:system"
    ))
    .claim("metadata", Map.of(
        "department", "Engineering",
        "team", "Backend Development", 
        "projects", Arrays.asList("project-a", "project-b", "project-c"),
        "skills", Arrays.asList("Java", "Python", "JavaScript", "SQL")
    ))
    .compressWith(Jwts.ZIP.DEF)
    .signWith(key)
    .compact();

// The payload is automatically compressed using DEFLATE

DEFLATE with Different JWT Types

// Compressed JWS (signed JWT)
String compressedJWS = Jwts.builder()
    .subject("user")
    .claim("largeData", generateLargeDataString(10000))
    .compressWith(Jwts.ZIP.DEF)
    .signWith(signatureKey)
    .compact();

// Compressed JWE (encrypted JWT)
SecretKey encryptionKey = Jwts.ENC.A256GCM.key().build();
SecretKey keyEncryptionKey = Jwts.KEY.A256KW.key().build();

String compressedJWE = Jwts.builder()
    .subject("confidential-user")
    .claim("sensitiveData", largeSensitivePayload)
    .claim("documents", listOfLargeDocuments)
    .compressWith(Jwts.ZIP.DEF)
    .encryptWith(keyEncryptionKey, Jwts.KEY.A256KW, Jwts.ENC.A256GCM)
    .compact();

// Compression happens before encryption for security

DEFLATE Algorithm Details

// Direct access to DEFLATE algorithm
DeflateCompressionAlgorithm deflateAlg = new DeflateCompressionAlgorithm();

// Algorithm properties
String algorithmId = deflateAlg.getId(); // "DEF"

// Manual compression/decompression (rarely needed)
String originalData = "Large payload data...";
byte[] compressed = deflateAlg.compress(originalData.getBytes());
byte[] decompressed = deflateAlg.decompress(compressed);

String recovered = new String(decompressed, StandardCharsets.UTF_8);
// recovered equals originalData

GZIP Compression

Basic GZIP Usage

// Create compressed JWT with GZIP (non-standard but widely supported)
String gzipCompressedJWT = Jwts.builder()
    .subject("user")
    .claim("bigPayload", massiveDataStructure)
    .claim("logs", extensiveLogData)
    .compressWith(Jwts.ZIP.GZIP)
    .signWith(key)
    .compact();

// GZIP typically provides similar compression ratios to DEFLATE
// but includes additional headers

GZIP Algorithm Details

// Direct access to GZIP algorithm
GzipCompressionAlgorithm gzipAlg = new GzipCompressionAlgorithm();

// Algorithm properties
String gzipId = gzipAlg.getId(); // "GZIP"

// Manual compression/decompression
byte[] gzipCompressed = gzipAlg.compress(originalData.getBytes());
byte[] gzipDecompressed = gzipAlg.decompress(gzipCompressed);

// GZIP includes checksums and headers for better error detection

Compression with Parsing

Parsing Compressed JWTs

// Parser automatically handles decompression
JwtParser parser = Jwts.parser()
    .verifyWith(key)
    .build();

// Parse compressed JWT - decompression is automatic
Jws<Claims> decompressedJws = parser.parseSignedClaims(compressedJWT);
Claims claims = decompressedJws.getPayload();

// Access decompressed claims normally
String subject = claims.getSubject();
List<String> permissions = claims.get("permissions", List.class);
Map<String, Object> metadata = claims.get("metadata", Map.class);

Compressed JWE Parsing

// Parse compressed and encrypted JWT
JwtParser encryptedParser = Jwts.parser()
    .decryptWith(keyEncryptionKey)
    .build();

// Automatic decryption and decompression
Jwe<Claims> decryptedJwe = encryptedParser.parseEncryptedClaims(compressedJWE);
Claims decryptedClaims = decryptedJwe.getPayload();

// Decompression happens after decryption
String sensitiveData = decryptedClaims.get("sensitiveData", String.class);

Advanced Compression Features

Compression Algorithm Selection

// Choose compression algorithm based on payload characteristics
public static CompressionAlgorithm selectCompressionAlgorithm(Object payload) {
    String jsonPayload = serializeToJson(payload);
    
    if (jsonPayload.length() < 1000) {
        // Small payloads might not benefit from compression
        return null;
    }
    
    // For most cases, DEFLATE is preferred (standard compliant)
    if (isStandardCompliantRequired()) {
        return Jwts.ZIP.DEF;
    }
    
    // GZIP for better error detection in unreliable networks
    if (requiresErrorDetection()) {
        return Jwts.ZIP.GZIP;
    }
    
    return Jwts.ZIP.DEF; // Default to DEFLATE
}

// Usage
CompressionAlgorithm algorithm = selectCompressionAlgorithm(largeClaims);
if (algorithm != null) {
    String jwt = Jwts.builder()
        .claims(largeClaims)
        .compressWith(algorithm)
        .signWith(key)
        .compact();
}

Compression Ratio Analysis

// Analyze compression effectiveness
public static CompressionStats analyzeCompression(Object payload) {
    String originalJson = serializeToJson(payload);
    byte[] originalBytes = originalJson.getBytes(StandardCharsets.UTF_8);
    
    // Test DEFLATE compression
    DeflateCompressionAlgorithm deflate = new DeflateCompressionAlgorithm();
    byte[] deflateCompressed = deflate.compress(originalBytes);
    
    // Test GZIP compression
    GzipCompressionAlgorithm gzip = new GzipCompressionAlgorithm();
    byte[] gzipCompressed = gzip.compress(originalBytes);
    
    return new CompressionStats(
        originalBytes.length,
        deflateCompressed.length,
        gzipCompressed.length,
        calculateRatio(originalBytes.length, deflateCompressed.length),
        calculateRatio(originalBytes.length, gzipCompressed.length)
    );
}

public static class CompressionStats {
    public final int originalSize;
    public final int deflateSize;
    public final int gzipSize;
    public final double deflateRatio;
    public final double gzipRatio;
    
    public CompressionStats(int original, int deflate, int gzip, 
                           double deflateRatio, double gzipRatio) {
        this.originalSize = original;
        this.deflateSize = deflate;
        this.gzipSize = gzip;
        this.deflateRatio = deflateRatio;
        this.gzipRatio = gzipRatio;
    }
}

private static double calculateRatio(int original, int compressed) {
    return (double) (original - compressed) / original * 100.0;
}

Security Considerations

Compression with Unsecured JWTs

// WARNING: Compression with unsecured JWTs can be a security risk
// Always use signature or encryption with compression

// Secure: Compression with signature
String secureCompressed = Jwts.builder()
    .subject("user")
    .claim("data", largeData)
    .compressWith(Jwts.ZIP.DEF)
    .signWith(secretKey) // Always sign compressed content
    .compact();

// More secure: Compression with encryption
String encryptedCompressed = Jwts.builder()
    .subject("user")
    .claim("sensitiveData", largeSensitiveData)
    .compressWith(Jwts.ZIP.DEF)
    .encryptWith(kekKey, Jwts.KEY.A256KW, Jwts.ENC.A256GCM)
    .compact();

Unsecured Decompression Control

// Control decompression of unsecured JWTs (use with extreme caution)
JwtParser unsecuredParser = Jwts.parser()
    .unsecured() // Allow unsecured JWTs
    .unsecuredDecompression() // Allow decompression of unsecured JWTs
    .build();

// This should only be used in very specific, controlled environments
// Prefer signed or encrypted compressed JWTs

Performance Optimization

Compression Threshold

// Only compress if payload exceeds threshold
public static String createOptimalJWT(Claims claims, SecretKey key) {
    String claimsJson = serializeToJson(claims);
    
    JwtBuilder builder = Jwts.builder()
        .claims(claims)
        .signWith(key);
    
    // Only compress if payload is large enough to benefit
    if (claimsJson.length() > 500) { // 500 byte threshold
        builder.compressWith(Jwts.ZIP.DEF);
    }
    
    return builder.compact();
}

Streaming Compression

// For very large payloads, consider streaming
public static String createStreamCompressedJWT(InputStream largePayload, 
                                              SecretKey key) {
    return Jwts.builder()
        .content(largePayload) // Stream directly
        .compressWith(Jwts.ZIP.DEF)
        .signWith(key)
        .compact();
}

// Parser handles streaming decompression automatically
public static String parseStreamCompressedJWT(String jwt, SecretKey key) {
    JwtParser parser = Jwts.parser()
        .verifyWith(key)
        .build();
    
    Jws<String> jws = parser.parseSignedContent(jwt);
    return jws.getPayload(); // Automatically decompressed
}

Custom Compression Algorithms

Extending Compression Registry

// Custom compression algorithm implementation
public class CustomCompressionAlgorithm implements CompressionAlgorithm {
    @Override
    public String getId() {
        return "CUSTOM";
    }
    
    @Override
    public byte[] compress(byte[] payload) throws CompressionException {
        // Custom compression implementation
        return compressWithCustomAlgorithm(payload);
    }
    
    @Override
    public byte[] decompress(byte[] compressed) throws CompressionException {
        // Custom decompression implementation
        return decompressWithCustomAlgorithm(compressed);
    }
}

// Register custom algorithm
CompressionAlgorithm customAlg = new CustomCompressionAlgorithm();

// Use with parser
JwtParser customParser = Jwts.parser()
    .verifyWith(key)
    .zip() // Access compression registry
        .add(customAlg)
        .and()
    .build();

// Use with builder
String customCompressedJWT = Jwts.builder()
    .subject("user")
    .claim("data", data)
    .compressWith(customAlg)
    .signWith(key)
    .compact();

Error Handling

Compression Errors

import io.jsonwebtoken.CompressionException;

try {
    String compressedJWT = Jwts.builder()
        .subject("user")
        .claim("corruptData", potentiallyCorruptData)
        .compressWith(Jwts.ZIP.DEF)
        .signWith(key)
        .compact();
        
} catch (CompressionException e) {
    // Handle compression failure
    log.error("Compression failed", e);
    
    // Fallback to uncompressed JWT
    String fallbackJWT = Jwts.builder()
        .subject("user")
        .claim("data", alternativeData)
        .signWith(key)
        .compact();
}

Decompression Errors

try {
    Jws<Claims> jws = parser.parseSignedClaims(compressedJWT);
    Claims claims = jws.getPayload();
    
} catch (CompressionException e) {
    // Handle decompression failure
    log.error("Decompression failed", e);
    throw new SecurityException("Invalid compressed token", e);
    
} catch (Exception e) {
    // Handle other parsing errors
    log.error("Token parsing failed", e);
    throw new IllegalArgumentException("Invalid token", e);
}

Compression Best Practices

When to Use Compression

// Ideal candidates for compression:
// 1. Large claim sets
Map<String, Object> largeClaims = Map.of(
    "permissions", Arrays.asList(/* many permissions */),
    "userProfile", Map.of(/* detailed profile */),
    "sessionData", Map.of(/* extensive session info */),
    "preferences", Map.of(/* many user preferences */)
);

// 2. Repeated/structured data
List<Map<String, Object>> structuredData = Arrays.asList(
    Map.of("id", 1, "name", "Item 1", "description", "Long description..."),
    Map.of("id", 2, "name", "Item 2", "description", "Another long description..."),
    // ... many similar items
);

// 3. JSON with repetitive keys
Map<String, Object> repetitiveData = Map.of(
    "configuration", Map.of(
        "feature_flag_1", true,
        "feature_flag_2", false,
        "feature_flag_3", true
        // ... many feature flags
    )
);

// Compression decision logic
public static boolean shouldCompress(Object payload) {
    String json = serializeToJson(payload);
    
    // Don't compress small payloads
    if (json.length() < 200) return false;
    
    // Always compress very large payloads
    if (json.length() > 2000) return true;
    
    // Analyze repetition for medium payloads
    return hasHighRepetition(json);
}

Performance Considerations

// Pre-compress reusable content
public class JWTCompressionCache {
    private final Map<String, byte[]> compressionCache = new ConcurrentHashMap<>();
    
    public String createCachedCompressedJWT(String cacheKey, Object payload, SecretKey key) {
        byte[] compressed = compressionCache.computeIfAbsent(cacheKey, k -> {
            String json = serializeToJson(payload);
            return Jwts.ZIP.DEF.compress(json.getBytes(StandardCharsets.UTF_8));
        });
        
        // Use pre-compressed content
        return Jwts.builder()
            .content(compressed)
            .header()
                .add("zip", "DEF") // Indicate compression
                .and()
            .signWith(key)
            .compact();
    }
}

The Compression functionality provides efficient payload reduction for JWTs while maintaining security and RFC compliance, with automatic handling in both creation and parsing scenarios.

Install with Tessl CLI

npx tessl i tessl/maven-io-jsonwebtoken--jjwt-impl

docs

compression.md

index.md

jwk-support.md

jwt-building.md

jwt-parsing.md

security-algorithms.md

utilities.md

tile.json