Base64 encode/decode · Guide

Base64 Decode Android: Kotlin and Java Without the Pitfalls

Android has two Base64 APIs with different defaults, and picking the wrong one produces output that looks right until a payload contains a newline. Here is which to use and why.

Two APIs

android.util.Base64 has been there since API 8 and takes an explicit flags argument. java.util.Base64 arrived with API 26 (Android 8.0) and matches server-side Java code exactly.

If your minSdk is 26 or higher, prefer java.util.Base64 — the same code then works in shared modules and on the JVM. Below that, use the Android one.

The flags that matter

Base64.DEFAULT adds line breaks every 76 characters when encoding. That breaks headers, JSON fields and anything expecting a single line, and it is the single most common Android Base64 bug:

Java
// encode — NO_WRAP, not DEFAULT
val encoded = android.util.Base64.encodeToString(bytes, android.util.Base64.NO_WRAP)

// decode
val bytes = android.util.Base64.decode(encoded, android.util.Base64.NO_WRAP)

// API 26+ — matches server-side Java
val encoded2 = java.util.Base64.getEncoder().encodeToString(bytes)
val bytes2   = java.util.Base64.getDecoder().decode(encoded2)

Decoding is more forgiving than encoding — it tolerates wrapped input either way — so the bug usually surfaces on the receiving system, not in your app.

base64url and JWTs

Tokens use the URL-safe alphabet with no padding. Decoding one with the standard decoder throws:

Java
// Android
android.util.Base64.decode(part, android.util.Base64.URL_SAFE or android.util.Base64.NO_PADDING)

// API 26+
java.util.Base64.getUrlDecoder().decode(part)

And converting bytes to a String always needs an explicit charset — String(bytes, Charsets.UTF_8) — since the platform default has changed across versions.

Frequently asked questions

Why does my encoded string have newlines?

Base64.DEFAULT wraps at 76 characters. Use Base64.NO_WRAP.

Which API should I use?

java.util.Base64 if minSdk is 26+, otherwise android.util.Base64.

Decoding a JWT part throws — why?

JWTs use base64url without padding. Use URL_SAFE with NO_PADDING, or getUrlDecoder().

Ready to try it?

Open the free browser-based Base64 encode/decode and apply what you just read — no sign-up, runs locally.

Open the Base64 encode/decode tool