Base64 Decode in Java — java.util.Base64 vs Apache Commons Codec
Java's relationship with Base64 has an awkward history. Before Java 8, the JDK had no built-in Base64 support — not in the standard library, not in javax.xml, not anywhere you'd expect to find it. Developers who needed Base64 had three options: write it manually (error-prone), use the internal (and undocumented) sun.misc.BASE64Decoder, or pull in Apache Commons Codec.
Java 8 finally added java.util.Base64, and the conversation shifted from "how do I decode this" to "should I remove the Commons Codec dependency."
java.util.Base64 — The Modern Standard
Introduced in Java 8, java.util.Base64 provides three encoder/decoder variants through static factory methods:
import java.util.Base64;
// Basic (standard) Base64
byte[] decoded = Base64.getDecoder().decode("aGVsbG8gd29ybGQ=");
System.out.println(new String(decoded)); // "hello world"
// URL-safe Base64 (for JWTs and URL parameters)
byte[] urlDecoded = Base64.getUrlDecoder().decode("aGVsbG8gd29ybGQ");
System.out.println(new String(urlDecoded)); // "hello world"
// MIME Base64 (lenient with line breaks)
byte[] mimeDecoded = Base64.getMimeDecoder().decode("aGVsbG8g\nd29ybGQ=");
System.out.println(new String(mimeDecoded)); // "hello world"
Encoding
// Basic
String encoded = Base64.getEncoder().encodeToString("hello world".getBytes());
// "aGVsbG8gd29ybGQ="
// URL-safe (no padding by default)
String urlEncoded = Base64.getUrlEncoder().withoutPadding().encodeToString("hello world".getBytes());
// "aGVsbG8gd29ybGQ" — no trailing ==
// MIME (76 characters per line with \r\n)
String mimeEncoded = Base64.getMimeEncoder().encodeToString(longText.getBytes());
The Three Variants Explained
| Variant | Characters | Padding | Line Wrapping | Use Case |
|---|---|---|---|---|
| Basic | A-Z, a-z, 0-9, +, / | Required | No | Standard Base64 |
| URL-safe | A-Z, a-z, 0-9, -, _ | Optional (use withoutPadding()) | No | JWTs, URL parameters |
| MIME | Same as Basic | Required | Yes (76 chars/line) | Email attachments |
Reading JWT Segments
JWTs use Base64URL encoding. Here's how to decode each segment in Java:
import java.util.Base64;
public class JwtDecoder {
public static void decodeJWT(String jwt) {
String[] parts = jwt.split("\\.");
if (parts.length != 3) {
throw new IllegalArgumentException("Invalid JWT format");
}
// Header (always Base64URL)
String header = new String(Base64.getUrlDecoder().decode(parts[0]));
System.out.println("Header: " + header);
// Payload (always Base64URL)
String payload = new String(Base64.getUrlDecoder().decode(parts[1]));
System.out.println("Payload: " + payload);
// Signature is binary, not meant to be decoded as text
byte[] signature = Base64.getUrlDecoder().decode(parts[2]);
System.out.println("Signature bytes: " + signature.length);
}
public static void main(String[] args) {
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
decodeJWT(token);
}
}
Output:
Header: {"alg":"HS256","typ":"JWT"}
Payload: {"sub":"1234567890","name":"John Doe","iat":1516239022}
Signature bytes: 32
Apache Commons Codec — The Legacy Option
Before Java 8, Apache Commons Codec was the de facto standard:
import org.apache.commons.codec.binary.Base64;
// Encode
String encoded = Base64.encodeBase64String("hello world".getBytes());
// "aGVsbG8gd29ybGQ="
// Decode
byte[] decoded = Base64.decodeBase64("aGVsbG8gd29ybGQ=");
System.out.println(new String(decoded)); // "hello world"
// URL-safe
String urlSafe = Base64.encodeBase64URLSafeString("hello world".getBytes());
// "aGVsbG8gd29ybGQ" — safe for URLs, no padding
When Commons Codec Still Wins
There are three scenarios where Commons Codec is still the better choice:
1. Supporting Java 7 and Below
If your project targets Java 7 or earlier (rare in 2026, but mainframes and legacy enterprise apps exist), java.util.Base64 isn't available.
2. Streaming With Larger Memory Footprint
Commons Codec's Base64InputStream and Base64OutputStream handle streaming encode/decode without loading the entire payload into memory:
import org.apache.commons.codec.binary.Base64InputStream;
import org.apache.commons.codec.binary.Base64OutputStream;
// Decode a large file without loading it entirely into memory
try (InputStream raw = new FileInputStream("large-file.b64");
InputStream decoded = new Base64InputStream(raw)) {
// Process the decoded stream
int b;
while ((b = decoded.read()) != -1) {
// handle byte
}
}
Java's java.util.Base64 has no built-in streaming support. You must decode the entire input at once.
3. Apache Tika and Ecosystem Compatibility
Some Java libraries (Apache Tika, Apache POI for older versions) depend on Commons Codec. If you're already pulling it in for other reasons, using it for Base64 operations adds zero overhead.
Performance Comparison
Benchmark: Encode and decode a 10 MB byte array
java.util.Base64:
Encode: ~45ms
Decode: ~40ms
Memory: ~13 MB for encoded output
Apache Commons Codec:
Encode: ~52ms
Decode: ~48ms
Memory: ~14 MB for encoded output
The built-in java.util.Base64 is slightly faster (about 15-20%) because it's implemented with sun.misc.Unsafe for direct memory access. Commons Codec is pure Java. For most applications, the difference is negligible.
Migration Guide: Commons Codec → java.util.Base64
| Operation | Commons Codec | java.util.Base64 |
|---|---|---|
| Standard encode | Base64.encodeBase64String(input) | Base64.getEncoder().encodeToString(input) |
| Standard decode | Base64.decodeBase64(input) | Base64.getDecoder().decode(input) |
| URL-safe encode | Base64.encodeBase64URLSafeString(input) | Base64.getUrlEncoder().withoutPadding().encodeToString(input) |
| URL-safe decode | Base64.decodeBase64(input) | Base64.getUrlDecoder().decode(input) |
| MIME encode | Base64.encodeBase64String(input, false, true) | Base64.getMimeEncoder().encodeToString(input) |
| MIME decode | Base64.decodeBase64(input) | Base64.getMimeDecoder().decode(input) |
Handling Checked Exceptions
Neither library throws checked exceptions for Base64 operations. Invalid input causes IllegalArgumentException from java.util.Base64 or returns null (Commons Codec's decodeBase64 on invalid input).
// java.util.Base64 — throws on invalid input
try {
byte[] result = Base64.getDecoder().decode("!!!invalid!!!");
} catch (IllegalArgumentException e) {
System.err.println("Invalid Base64 input: " + e.getMessage());
}
// Commons Codec — returns null on invalid input
byte[] result = Base64.decodeBase64("!!!invalid!!!");
if (result == null) {
System.err.println("Invalid Base64 input");
}
Real-World Example: Decoding a File Upload
import java.util.Base64;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Base64FileHandler {
public static byte[] decodeBase64File(String base64FilePath) throws Exception {
String content = new String(Files.readAllBytes(Paths.get(base64FilePath)));
// Remove any data URI prefix if present
if (content.contains(",")) {
content = content.split(",")[1];
}
// Remove whitespace that may have been introduced by MIME wrapping
content = content.replaceAll("\\s+", "");
return Base64.getDecoder().decode(content);
}
public static void saveDecodedFile(String base64FilePath, String outputPath) throws Exception {
byte[] decoded = decodeBase64File(base64FilePath);
Files.write(Paths.get(outputPath), decoded);
System.out.println("Decoded file saved to: " + outputPath);
}
}
FAQ
Which Base64 library should I use in new Java projects?
java.util.Base64. It's built-in, well-tested, slightly faster, and requires no external dependencies. Use it for all Java 8+ projects.
Does java.util.Base64 support URL-safe encoding?
Yes. Base64.getUrlEncoder().withoutPadding().encodeToString(input) produces Base64URL output. Base64.getUrlDecoder().decode(input) handles both standard and URL-safe Base64 on input.
Can I decode Base64 without padding in Java?
Yes. Base64.getDecoder() requires padding by default, but Base64.getUrlDecoder() is lenient with missing padding. For standard Base64 with missing padding, append the needed = characters or wrap the input to the correct length.
Is Apache Commons Codec still maintained?
Yes, Commons Codec is actively maintained as part of the Apache Commons project. The latest version (1.17 as of 2026) adds no significant new Base64 features but continues to receive bug fixes and compatibility updates.
How does Base64 compare between Java and other languages?
Java's java.util.Base64 matches the Base64 specification exactly. Decoding a Base64 string encoded in Python, JavaScript, or Go produces the same result in Java, provided the encoding variant (standard vs URL-safe) is consistent. The Base64 Encoder & Decoder tool can help verify cross-language consistency.
For debugging Base64 strings you encounter in Java APIs, the Base64 Encoder & Decoder tool handles standard and URL-safe formats. If you're working with JWTs in Java, the JWT Decoder can decode and validate tokens before you write the server-side verification logic.