ToolDeck

ถอดรหัส Base64 ใน Java 8+

·Backend Engineer·ตรวจสอบโดยAisha Osei·เผยแพร่เมื่อ

ใช้ ถอดรหัส Base64 ออนไลน์ ฟรีโดยตรงในเบราว์เซอร์ของคุณ — ไม่ต้องติดตั้ง

ลอง ถอดรหัส Base64 ออนไลน์ ออนไลน์ →

การถอดรหัส Base64 ใน Java เป็นสิ่งที่ผมต้องใช้แทบทุกสองสามวัน — ไม่ว่าจะดึง secret จาก Kubernetes environment variable อ่าน binary payload จาก REST API หรือตรวจสอบ JWT token ระหว่าง debug session คลาส java.util.Base64 ที่มาพร้อม JDK 8 มี decoder สามแบบ: getDecoder() สำหรับ Base64 มาตรฐาน, getUrlDecoder() สำหรับ input แบบ URL-safe และ getMimeDecoder() สำหรับข้อมูลที่มีการตัดบรรทัด เช่น email attachment หากต้องการตรวจสอบแบบด่วนโดยไม่เขียนโค้ด Base64 decoder ของ ToolDeck จัดการได้ทันทีในเบราว์เซอร์ คู่มือนี้มุ่งเป้าที่ Java 8+ และครอบคลุม decoder ทั้งสามแบบ การ streaming ด้วย wrap(InputStream), การแยก JWT payload, การถอดรหัสไฟล์และ API response, Apache Commons Codec เป็นทางเลือก และข้อผิดพลาดสี่อย่างที่ทำให้ output เสียหายใน production

  • Base64.getDecoder().decode(s) คือวิธีมาตรฐาน — มีใน java.util.Base64 ตั้งแต่ JDK 8 ไม่ต้องติดตั้ง dependency เพิ่ม
  • ใช้ getUrlDecoder() สำหรับ JWT token และ OAuth payload — ใช้ตัวอักษร - และ _ ไม่ใช่ + และ /
  • getMimeDecoder() ละเว้น line break และ whitespace จึงเหมาะกับ email attachment และ PEM certificate
  • decoder.wrap(InputStream) ถอดรหัสแบบ on the fly สำหรับไฟล์ขนาดใหญ่โดยไม่โหลดทุกอย่างลงหน่วยความจำ
  • basic decoder มีความเข้มงวด — trailing newline, space หรืออักขระ alphabet ผิดประเภทจะ throw IllegalArgumentException ทันที

Base64 Decoding คืออะไร?

การเข้ารหัส Base64 แปลงข้อมูล binary เป็น ASCII ที่ประกอบด้วย 64 อักขระ เพื่อให้ส่งผ่าน ช่องทางที่รองรับเฉพาะข้อความได้อย่างปลอดภัย ไม่ว่าจะเป็น JSON field, HTTP header, XML document หรือ email body การถอดรหัสทำกระบวนการย้อนกลับ: Base64 ทุก 4 อักขระ จะแมปกลับเป็น 3 byte ดั้งเดิม = padding ที่ท้ายบอกจำนวน byte ที่เติมเพื่อให้กลุ่มสุดท้ายครบ Base64 ไม่ใช่การเข้ารหัสลับ — ใครก็ย้อนกลับได้ จุดประสงค์คือความปลอดภัยในการส่งข้อมูล ไม่ใช่การรักษาความลับ

กรณีการถอดรหัสที่พบบ่อยใน Java: ดึงค่า configuration ที่ inject มาเป็น Base64 environment variable, แกะ binary file content จาก cloud API response, อ่าน PEM-encoded certificate และตรวจสอบ JWT token payload ระหว่าง debug

Before · text
After · text
ZGItcHJvZC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbTo1NDMy
db-prod.us-east-1.amazonaws.com:5432

Base64.getDecoder().decode() — วิธีถอดรหัสมาตรฐาน

คลาส java.util.Base64 ถูกเพิ่มใน JDK 8 และแทนที่ sun.misc.BASE64Decoder เก่าที่ทุกคนเคยพึ่งพา ไม่ต้องการ external dependency — แค่ import java.util.Base64 แล้วเรียก Base64.getDecoder().decode() เมธอดรับทั้ง String และ byte[] และคืนค่าเป็น byte[] ของข้อมูลที่ถอดรหัสแล้ว

ตัวอย่างเบื้องต้น

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class DecodeCredential {
    public static void main(String[] args) {
        // Kubernetes secret value, Base64-encoded
        String encoded = "ZGItcHJvZC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbTo1NDMy";

        byte[] decodedBytes = Base64.getDecoder().decode(encoded);
        String connectionString = new String(decodedBytes, StandardCharsets.UTF_8);

        System.out.println(connectionString);
        // db-prod.us-east-1.amazonaws.com:5432
    }
}

ระบุ StandardCharsets.UTF_8 เสมอเมื่อสร้าง String constructor แบบ new String(bytes) ที่ไม่ระบุ charset ใช้ encoding ค่าเริ่มต้นของ platform ซึ่งแตกต่างกันในแต่ละระบบ บน Windows server ที่ใช้ Cp1252 เป็นค่าเริ่มต้น อักขระ UTF-8 แบบ multi-byte จะเสียหายโดยไม่มีการแจ้งเตือน

การตรวจสอบแบบ round-trip

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class RoundTrip {
    public static void main(String[] args) {
        String original = "redis://cache-prod.internal:6379/session-store";

        String encoded = Base64.getEncoder().encodeToString(
            original.getBytes(StandardCharsets.UTF_8)
        );
        System.out.println(encoded);
        // cmVkaXM6Ly9jYWNoZS1wcm9kLmludGVybmFsOjYzNzkvc2Vzc2lvbi1zdG9yZQ==

        byte[] decoded = Base64.getDecoder().decode(encoded);
        String recovered = new String(decoded, StandardCharsets.UTF_8);

        System.out.println(recovered.equals(original)); // true
    }
}

การถอดรหัสลง buffer ที่จัดสรรไว้ล่วงหน้า

overload แบบสามอาร์กิวเมนต์ decode(byte[] src, byte[] dst) เขียนผลลัพธ์ลงใน destination buffer โดยตรงและคืนจำนวน byte ที่เขียน ช่วยหลีกเลี่ยง การจัดสรรหน่วยความจำเพิ่มเติมในเส้นทางที่ถูกเรียกบ่อย:

Java 8+
import java.util.Base64;

public class DecodeToBuffer {
    public static void main(String[] args) {
        byte[] src = "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=".getBytes();
        byte[] dst = new byte[1024]; // pre-allocated

        int len = Base64.getDecoder().decode(src, dst);
        String result = new String(dst, 0, len);

        System.out.println(result);
        // {"host":"10.0.1.50","port":8443}
    }
}
หมายเหตุ:decode() จะ throw IllegalArgumentException หาก input มีอักขระที่ไม่อยู่ใน Base64 alphabet (รวมถึง line break และ space) ถ้า input อาจมี whitespace ให้เปลี่ยนเป็น getMimeDecoder() หรือ ทำ strip ด้วย encoded.strip() ก่อนถอดรหัส

การถอดรหัส Base64 กับประเภทข้อมูลที่ไม่ใช่มาตรฐานและ Custom Object

byte[] ดิบที่ได้จาก decode() มักต้องแปลงเป็นสิ่งที่เฉพาะเจาะจงกว่า: UUID, Java object ที่ serialize แล้ว, protobuf message หรือ timestamp decoder เองคืนแค่ byte เสมอ — การแปลงเป็น domain type เป็นหน้าที่ของคุณ

Base64 เป็น UUID

Java 8+
import java.util.Base64;
import java.nio.ByteBuffer;
import java.util.UUID;

public class DecodeUUID {
    public static UUID fromBase64(String encoded) {
        byte[] bytes = Base64.getUrlDecoder().decode(encoded);
        ByteBuffer bb = ByteBuffer.wrap(bytes);
        return new UUID(bb.getLong(), bb.getLong());
    }

    public static void main(String[] args) {
        // Compact Base64-encoded UUID from an API response
        String encoded = "f47ac10b-58cc-4372-a567-0e02b2c3d479";
        UUID original = UUID.fromString(encoded);

        // Encode to Base64 (compact form — 22 chars vs 36)
        ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
        bb.putLong(original.getMostSignificantBits());
        bb.putLong(original.getLeastSignificantBits());
        String compact = Base64.getUrlEncoder().withoutPadding()
            .encodeToString(bb.array());
        System.out.println(compact); // 9HrBC1jMQ3KlZw4CssPUeQ

        // Decode back
        UUID recovered = fromBase64(compact);
        System.out.println(recovered); // f47ac10b-58cc-4372-a567-0e02b2c3d479
    }
}

Base64 เป็น JSON object ที่ deserialize ด้วย Jackson

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.Instant;

public class DecodeJsonPayload {
    record DeployEvent(String service, String region, Instant deployedAt, int replicas) {}

    public static void main(String[] args) throws Exception {
        // Base64-encoded JSON payload from a message queue
        String encoded = "eyJzZXJ2aWNlIjoicGF5bWVudC1nYXRld2F5Iiwi"
            + "cmVnaW9uIjoiZXUtd2VzdC0xIiwiZGVwbG95ZWRBdCI6"
            + "IjIwMjYtMDMtMTVUMTQ6MzA6MDBaIiwicmVwbGljYXMiOjR9";

        byte[] jsonBytes = Base64.getDecoder().decode(encoded);
        String json = new String(jsonBytes, StandardCharsets.UTF_8);
        System.out.println(json);
        // {"service":"payment-gateway","region":"eu-west-1",
        //  "deployedAt":"2026-03-15T14:30:00Z","replicas":4}

        ObjectMapper mapper = new ObjectMapper();
        mapper.findAndRegisterModules(); // picks up JavaTimeModule
        DeployEvent event = mapper.readValue(jsonBytes, DeployEvent.class);

        System.out.println(event.service());   // payment-gateway
        System.out.println(event.deployedAt()); // 2026-03-15T14:30:00Z
    }
}
คำเตือน:ห้ามใช้ ObjectInputStream เพื่อ deserialize ข้อมูล Base64 ที่ไม่น่าเชื่อถือ การโจมตีผ่าน Java deserialization มีบันทึกไว้ชัดเจน — ถ้า encoded content มาจากแหล่งภายนอก ให้ parse เป็น JSON หรือ protobuf แทนการใช้ native Java serialization

เอกสารอ้างอิงเมธอดของ Base64.Decoder

ทุกเมธอดอยู่ใน java.util.Base64 และ inner class Base64.Decoder factory method สามแบบบน Base64 คืน decoder instance ที่แตกต่างกัน ส่วนเมธอด decode() และ wrap() อยู่บน Decoder instance

เมธอด
ค่าที่คืน
ประเภท Input
คำอธิบาย
getDecoder()
Base64.Decoder
Decoder มาตรฐาน (RFC 4648 §4 ใช้ตัวอักษร + และ / พร้อม padding =)
getUrlDecoder()
Base64.Decoder
Decoder แบบ URL-safe (RFC 4648 §5 ใช้ตัวอักษร - และ _ พร้อม padding =)
getMimeDecoder()
Base64.Decoder
MIME decoder — ละเว้นตัวคั่นบรรทัดและอักขระที่ไม่ใช่ Base64
decode(String src)
byte[]
String
ถอดรหัส string ที่รับเข้ามาและคืนค่าเป็น byte array ใหม่
decode(byte[] src)
byte[]
byte[]
ถอดรหัส byte array ที่รับเข้ามาและคืนค่าเป็น byte array ใหม่
decode(byte[] src, byte[] dst)
int
byte[] + byte[]
ถอดรหัสลงใน buffer ปลายทางที่จัดสรรไว้ล่วงหน้า และคืนจำนวน byte ที่เขียน
wrap(InputStream is)
InputStream
InputStream
คืน stream ที่ถอดรหัสข้อมูล Base64 แบบ on the fly

getMimeDecoder() — ถอดรหัส Base64 แบบ MIME ที่มีการตัดบรรทัด

basic decoder ปฏิเสธทุกอย่างที่ไม่อยู่ใน Base64 alphabet — รวมถึง line break \r\n ที่ MIME-encoded content มักมีอยู่ Email attachment, PEM certificate และ API response เก่าบางตัวตัดบรรทัด Base64 ทุก 76 อักขระ getMimeDecoder() ละเว้น line separator และอักขระที่ไม่อยู่ใน Base64 alphabet โดยอัตโนมัติ จึงรองรับกรณีนี้ได้ทันที

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class MimeDecode {
    public static void main(String[] args) {
        // PEM certificate body — line-wrapped at 76 characters
        String pemBody = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t\r\n"
            + "TUlJQm96Q0NBVWlnQXdJQkFnSUpBSXBhVDJU\r\n"
            + "aVFvZU1BMEdDU3FHU0liM0RRRU==";

        // getDecoder() would throw IllegalArgumentException here
        byte[] decoded = Base64.getMimeDecoder().decode(pemBody);
        System.out.println(new String(decoded, StandardCharsets.UTF_8));
        // -----BEGIN CERTIFICATE-----
        // MIIBozCCAUigAwIBAgIJAIpaT2T...
    }
}
หมายเหตุ:getMimeDecoder() มีความยืดหยุ่น: จะข้ามอักขระที่ไม่ถูกต้องแทนการ throw exception เหมาะสำหรับข้อมูล MIME ที่รู้จัก แต่อาจกลืน data corruption ในอย่างเงียบๆ สำหรับ input ทั่วไป ใช้ getDecoder() เมื่อต้องการ validation ที่เข้มงวด

ถอดรหัส Base64 จากไฟล์และ API Response

อ่านไฟล์ที่เข้ารหัส Base64 จากดิสก์

บางครั้ง binary file (รูปภาพ, certificate, encrypted blob) ถูกเก็บบนดิสก์ ในรูปแบบ Base64 text อ่านไฟล์, ถอดรหัส, แล้วเขียน binary output:

Java 8+
import java.util.Base64;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class DecodeFile {
    public static void main(String[] args) {
        Path inputPath = Path.of("tls-cert.pem.b64");
        Path outputPath = Path.of("tls-cert.pem");

        try {
            String encoded = Files.readString(inputPath).strip();
            byte[] decoded = Base64.getMimeDecoder().decode(encoded);
            Files.write(outputPath, decoded);

            System.out.printf("Decoded %d bytes → %s%n", decoded.length, outputPath);
        } catch (IOException e) {
            System.err.println("File error: " + e.getMessage());
        } catch (IllegalArgumentException e) {
            System.err.println("Invalid Base64: " + e.getMessage());
        }
    }
}

ถอดรหัส Base64 field จาก HTTP API response

Cloud API (AWS KMS, GitHub Contents, Vault) มักคืน binary data เป็น Base64 string ภายใน JSON Parse JSON ก่อน แล้วถอดรหัส field ที่ต้องการ:

Java 11+
import java.util.Base64;
import java.nio.charset.StandardCharsets;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class DecodeApiResponse {
    public static void main(String[] args) {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.example.com/secrets/db-password"))
            .header("Authorization", "Bearer sk-prod-9f8e7d6c")
            .build();

        try {
            HttpResponse<String> response = client.send(
                request, HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() != 200) {
                System.err.printf("Unexpected status: %d%n", response.statusCode());
                return;
            }

            ObjectMapper mapper = new ObjectMapper();
            JsonNode root = mapper.readTree(response.body());

            // API returns: {"name":"db-password","value":"cG9zdGdyZXM6eGs5...","version":3}
            String encodedValue = root.get("value").asText();
            byte[] decoded = Base64.getDecoder().decode(encodedValue);
            String secret = new String(decoded, StandardCharsets.UTF_8);

            System.out.println("Secret: " + secret);
            // Secret: postgres:xk9mP2qR@db-prod:5432/orders
        } catch (Exception e) {
            System.err.println("Failed to fetch secret: " + e.getMessage());
        }
    }
}
หมายเหตุ:ครอบ decode call ใน try-catch แยกสำหรับ IllegalArgumentException ต่างหากจาก network error การผสม I/O exception กับ decoding failure ทำให้ debug ยากขึ้น — คุณต้องรู้ทันทีว่า API คืนข้อมูลผิดหรือ network ล้มเหลว

การถอดรหัส Base64 จาก Command Line

บางครั้งไม่จำเป็นต้องเขียน Java program ทุก Linux และ macOS มีคำสั่ง base64 และ JDK 9+ มี jshell สำหรับ Java one-liner แบบ interactive เมื่อต้องการตรวจสอบระหว่าง debug วิธีเหล่านี้เร็วกว่าการ compile class

bash
# Decode a Base64 string (Linux / macOS)
echo "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=" | base64 --decode
# {"host":"10.0.1.50","port":8443}

# Decode and pretty-print with jq
echo "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=" | base64 --decode | jq .
# {
#   "host": "10.0.1.50",
#   "port": 8443
# }

# Quick decode with jshell (JDK 9+)
echo 'System.out.println(new String(java.util.Base64.getDecoder().decode("c2VydmVyLWNvbmZpZw==")))' | jshell -
# server-config

# macOS uses -D instead of --decode
echo "c2VydmVyLWNvbmZpZw==" | base64 -D

สำหรับการวาง encoded string ลงในเบราว์เซอร์โดยตรง Base64 decoder ของ ToolDeck รองรับทั้งรูปแบบมาตรฐานและ URL-safe โดยไม่ต้องตั้งค่าใดๆ

ทางเลือกประสิทธิภาพสูง: Apache Commons Codec

java.util.Base64 ที่มาพร้อม Java ได้รับการ optimize อย่างดีอยู่แล้ว — JDK 11+ ใช้ intrinsic บน x86 สำหรับ encoding และ decoding สำหรับแอปพลิเคชันส่วนใหญ่ ไม่มีเหตุผลต้องใช้ third-party library อย่างไรก็ตาม Apache Commons Codec ยังคงนิยม ใน legacy codebase และมี Base64InputStream สำหรับ streaming decoding พร้อมการจัดการ whitespace อัตโนมัติ

XML (Maven)
<!-- pom.xml -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.17.0</version>
</dependency>
Java 8+
import org.apache.commons.codec.binary.Base64;

public class CommonsCodecDecode {
    public static void main(String[] args) {
        // Commons Codec is more lenient — handles whitespace and line breaks
        String encoded = "eyJob3N0IjoiMTAuMC4xLjUw\nIiwicG9ydCI6ODQ0M30=";
        byte[] decoded = Base64.decodeBase64(encoded);

        System.out.println(new String(decoded));
        // {"host":"10.0.1.50","port":8443}
    }
}

ข้อได้เปรียบหลักของ Commons Codec เหนือ built-in API คือความยืดหยุ่นกับ whitespace โดยค่าเริ่มต้น และคลาส Base64InputStream ที่มีมาก่อน decoder.wrap() ของ Java ถ้าใช้ Java 8+ built-in API ครอบคลุมทุกสิ่งที่ Commons Codec ทำได้ ผมเลือกใช้ Commons Codec เฉพาะเมื่อ project มี dependency นั้นอยู่แล้ว

Streaming ไฟล์ Base64 ขนาดใหญ่ด้วย decoder.wrap()

การโหลดไฟล์ Base64 ขนาด 200 MB ด้วย Files.readString() แล้วเรียก decode() จะจัดสรร heap ประมาณ 350 MB: ทั้ง encoded string และ decoded byte array decoder.wrap(InputStream) ถอดรหัสแบบ on the fly ทำให้การใช้หน่วยความจำคงที่

Java 8+
import java.util.Base64;
import java.io.*;
import java.nio.file.*;

public class StreamDecode {
    public static void main(String[] args) throws IOException {
        Path src = Path.of("database-dump.sql.b64");
        Path dst = Path.of("database-dump.sql");

        try (InputStream in = Base64.getMimeDecoder().wrap(
                 new BufferedInputStream(Files.newInputStream(src)));
             OutputStream out = new BufferedOutputStream(Files.newOutputStream(dst))) {

            byte[] buffer = new byte[8192];
            int bytesRead;
            long total = 0;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
                total += bytesRead;
            }
            System.out.printf("Decoded %d bytes → %s%n", total, dst);
        }
    }
}

บน Java 9+ สามารถแทน read loop ด้วย in.transferTo(out) — ทำสิ่งเดียวกัน ด้วยโค้ดน้อยกว่า ใช้ getMimeDecoder().wrap() แทน getDecoder().wrap() ถ้าไฟล์อาจมี line break (PEM file, email export)

Java 9+
import java.util.Base64;
import java.io.*;
import java.nio.file.*;

public class StreamDecodeSimple {
    public static void main(String[] args) throws IOException {
        try (InputStream in = Base64.getMimeDecoder().wrap(
                 new BufferedInputStream(Files.newInputStream(Path.of("backup.tar.b64"))));
             OutputStream out = Files.newOutputStream(Path.of("backup.tar"))) {
            in.transferTo(out); // Java 9+
        }
    }
}
คำเตือน:getDecoder().wrap() ไม่ รองรับ line break ใน stream ถ้าข้อมูล Base64 มี newline (ตัดบรรทัดทุก 76 อักขระ) ให้ใช้ getMimeDecoder().wrap() แทน — มิฉะนั้น stream จะผลิต output เสียหาย โดยไม่มีการแจ้งเตือน หรือ throw exception ในตำแหน่ง read ที่ไม่แน่นอน

ถอดรหัส JWT Payload ใน Java โดยไม่ใช้ JWT Library

JWT ประกอบด้วยสามส่วนที่เข้ารหัส Base64url คั่นด้วยจุด ส่วนกลางคือ payload — ส่วนที่สำคัญระหว่าง debug คุณถอดรหัสได้โดยไม่ต้องดึง jjwt หรือ Nimbus มาใช้ แค่ split ด้วย ., ถอดรหัสส่วนที่สองด้วย getUrlDecoder(), แล้ว parse JSON ที่ได้:

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class JWTInspect {
    public static String decodeJwtPayload(String token) {
        String[] parts = token.split("\\.");
        if (parts.length != 3) {
            throw new IllegalArgumentException(
                "Invalid JWT: expected 3 segments, got " + parts.length);
        }
        // JWT uses URL-safe Base64 without padding
        byte[] payload = Base64.getUrlDecoder().decode(parts[1]);
        return new String(payload, StandardCharsets.UTF_8);
    }

    public static void main(String[] args) {
        String token = "eyJhbGciOiJSUzI1NiJ9"
            + ".eyJzdWIiOiJ1c3ItNjcyIiwiaXNzIjoiYXV0aC5leGFtcGxlLmNvbSIs"
            + "ImV4cCI6MTc0MTk1NjgwMCwicm9sZXMiOlsiYWRtaW4iLCJiaWxsaW5nIl19"
            + ".SIGNATURE_PLACEHOLDER";

        String payload = decodeJwtPayload(token);
        System.out.println(payload);
        // {"sub":"usr-672","iss":"auth.example.com",
        //  "exp":1741956800,"roles":["admin","billing"]}
    }
}

ข้อผิดพลาดที่พบบ่อย

ผมเจอทุกข้อนี้ในการ code review และสองข้อแรกเป็นสาเหตุส่วนใหญ่ ของ bug ที่เกี่ยวกับ Base64 ใน Java service บน production

ใช้ getDecoder() กับ input แบบ URL-safe

ปัญหา: JWT token และ OAuth access token ใช้ตัวอักษรแบบ URL-safe (- และ _) การส่งให้ getDecoder() จะ throw IllegalArgumentException เพราะ - ไม่อยู่ใน Base64 alphabet มาตรฐาน

วิธีแก้: ตรวจสอบแหล่งข้อมูล: token จากระบบ auth ต้องใช้ getUrlDecoder(); MIME attachment ต้องใช้ getMimeDecoder()

Before · Java
After · Java
// JWT header — URL-safe, no padding
String header = "eyJhbGciOiJSUzI1NiJ9";
byte[] decoded = Base64.getDecoder().decode(header);
// IllegalArgumentException: Illegal base64 character 2d
String header = "eyJhbGciOiJSUzI1NiJ9";
byte[] decoded = Base64.getUrlDecoder().decode(header);
System.out.println(new String(decoded));
// {"alg":"RS256"}
ไม่ระบุ charset เมื่อสร้าง String

ปัญหา: new String(bytes) ใช้ default charset ของ JVM ซึ่งแตกต่างกันในแต่ละ environment Linux CI server (UTF-8) และ Windows production host (Cp1252) ให้ผลลัพธ์ต่างกันสำหรับ byte เดียวกัน

วิธีแก้: ระบุ StandardCharsets.UTF_8 เป็นอาร์กิวเมนต์ที่สองเสมอ

Before · Java
After · Java
byte[] decoded = Base64.getDecoder().decode(encoded);
String result = new String(decoded);
// platform-dependent — may corrupt multi-byte characters
byte[] decoded = Base64.getDecoder().decode(encoded);
String result = new String(decoded, StandardCharsets.UTF_8);
// consistent across all platforms
ถอดรหัส string ที่มี trailing whitespace

ปัญหา: Base64 string ที่วางจาก terminal หรืออ่านจาก config file มักมี trailing newline basic decoder ปฏิเสธอักขระทุกตัวที่ไม่อยู่ใน Base64 alphabet

วิธีแก้: เรียก .strip() บน input ก่อนถอดรหัส หรือเปลี่ยนเป็น getMimeDecoder() ซึ่งละเว้น whitespace

Before · Java
After · Java
// Read from environment variable — has a trailing newline
String encoded = System.getenv("DB_PASSWORD_B64"); // "cG9zdGdyZXM=
"
byte[] decoded = Base64.getDecoder().decode(encoded);
// IllegalArgumentException: Illegal base64 character a
String encoded = System.getenv("DB_PASSWORD_B64");
byte[] decoded = Base64.getDecoder().decode(encoded.strip());
System.out.println(new String(decoded, StandardCharsets.UTF_8));
// postgres
แปลง binary data เป็น String

ปัญหา: การเรียก new String(decoded) บน binary content (รูปภาพ, protobuf, encrypted blob) ทำให้ String ไม่ถูกต้อง การแปลงกลับเป็น byte ในภายหลังจะเสียหายโดยไม่มีการแจ้งเตือน เพราะ String constructor แทนที่ UTF-8 sequence ที่ไม่ถูกต้อง

วิธีแก้: เก็บ binary data เป็น byte[] ตลอด pipeline ทั้งหมด แปลงเป็น String เฉพาะเมื่อรู้ว่า content เป็น text

Before · Java
After · Java
byte[] decoded = Base64.getDecoder().decode(pngBase64);
String imageStr = new String(decoded); // corrupts binary
Files.writeString(Path.of("image.png"), imageStr); // broken file
byte[] decoded = Base64.getDecoder().decode(pngBase64);
// Write bytes directly — no String conversion
Files.write(Path.of("image.png"), decoded);

เปรียบเทียบเมธอด

built-in decoder ครอบคลุม use case ส่วนใหญ่ Apache Commons Codec และ Guava เป็นทางเลือกที่อาจพบใน legacy codebase

เมธอด
รูปแบบการเข้ารหัส
ละเว้น Whitespace
Streaming
Custom Types
ต้องติดตั้งเพิ่ม
Base64.getDecoder()
มาตรฐาน (+, /)
ไม่ (JDK 8+)
Base64.getUrlDecoder()
URL-safe (-, _)
ไม่ (JDK 8+)
Base64.getMimeDecoder()
MIME (รองรับ line break)
ไม่ (JDK 8+)
decoder.wrap(InputStream)
ทุกรูปแบบ
ขึ้นอยู่กับ decoder
ไม่ (JDK 8+)
Apache Commons Base64InputStream
มาตรฐาน / URL-safe
ใช่ (commons-codec)
Apache Commons Base64.decodeBase64()
มาตรฐาน
ใช่ (commons-codec)
Guava BaseEncoding.base64().decode()
มาตรฐาน
ใช่ (guava)

สำหรับ JWT token และ API payload สมัยใหม่: getUrlDecoder() สำหรับ email attachment และ PEM certificate: getMimeDecoder() สำหรับไฟล์ขนาดใหญ่ ที่หน่วยความจำสำคัญ: decoder.wrap(InputStream) นอกนั้น: getDecoder() Apache Commons Codec สมเหตุสมผลเฉพาะเมื่อมีอยู่ใน dependency tree แล้ว

สำหรับการตรวจสอบรวดเร็วระหว่าง development online Base64 decoder เร็วกว่าการเขียน one-off class

คำถามที่พบบ่อย

จะถอดรหัส Base64 string ใน Java ได้อย่างไร?

import java.util.Base64 แล้วเรียก Base64.getDecoder().decode(encodedString) เมธอดนี้คืนค่าเป็น byte[] — ใช้ new String(bytes, StandardCharsets.UTF_8) เพื่อแปลงเป็นข้อความที่อ่านได้ สำหรับ Base64 แบบ URL-safe (ที่ใช้ใน JWT) ให้เปลี่ยน getDecoder() เป็น getUrlDecoder()

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

byte[] decoded = Base64.getDecoder().decode("c2VydmVyLWNvbmZpZw==");
String result = new String(decoded, StandardCharsets.UTF_8);
System.out.println(result); // server-config

getDecoder() กับ getMimeDecoder() ใน Java ต่างกันอย่างไร?

getDecoder() มีความเข้มงวด — จะปฏิเสธอักขระทุกตัวที่ไม่อยู่ใน Base64 alphabet รวมถึง line break ส่วน getMimeDecoder() รองรับตัวคั่นบรรทัด (\r\n) และละเว้นอักขระที่ไม่ใช่ Base64 จึงเหมาะสำหรับการถอดรหัส email attachment และ PEM certificate ที่มีการตัดบรรทัดทุก 76 อักขระ

Java 8+
String wrapped = "c2VydmVyLWNv\r\nbmZpZw==";

// getDecoder() throws IllegalArgumentException
// Base64.getDecoder().decode(wrapped); // FAILS

// getMimeDecoder() handles it
byte[] decoded = Base64.getMimeDecoder().decode(wrapped);
System.out.println(new String(decoded)); // server-config

จะถอดรหัส Base64 แบบ URL-safe ใน Java ได้อย่างไร?

ใช้ Base64.getUrlDecoder().decode(encoded) URL decoder รองรับตัวอักษร - และ _ ตาม RFC 4648 §5 แทน + และ / JWT token ใช้ตัวอักษรชุดนี้เสมอ หาก padding (=) ถูกตัดออก (ซึ่งพบบ่อยใน JWT) URL decoder ของ Java ก็ยังรองรับได้ ทั้งแบบมี padding และไม่มี padding

Java 8+
import java.util.Base64;

// JWT header — URL-safe, no padding
String jwtHeader = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
byte[] decoded = Base64.getUrlDecoder().decode(jwtHeader);
System.out.println(new String(decoded));
// {"alg":"HS256","typ":"JWT"}

จะถอดรหัส Base64 แบบ streaming สำหรับไฟล์ขนาดใหญ่ใน Java ได้อย่างไร?

ใช้ decoder.wrap(inputStream) เพื่อ wrap FileInputStream InputStream ที่ได้จะถอดรหัส Base64 แบบ on the fly ขณะอ่าน byte ทำให้การใช้หน่วยความจำคงที่โดยไม่ขึ้นกับขนาดไฟล์ สามารถต่อ pipe กับ BufferedInputStream หรือส่งตรงไปยัง Files.copy() เพื่อประสิทธิภาพสูงสุด

Java 8+
import java.util.Base64;
import java.io.*;
import java.nio.file.*;

try (InputStream in = Base64.getDecoder().wrap(
        new BufferedInputStream(new FileInputStream("payload.b64")));
     OutputStream out = new FileOutputStream("payload.bin")) {
    in.transferTo(out);
}

ทำไม Base64.getDecoder().decode() ถึง throw IllegalArgumentException?

basic decoder มีความเข้มงวด: จะปฏิเสธ line break, space และอักขระทุกตัวที่ไม่อยู่ใน A-Za-z0-9+/= สาเหตุที่พบบ่อยมี 3 อย่าง: input มี trailing newline (ให้ trim ก่อน), input ใช้ตัวอักษรแบบ URL-safe เช่น - และ _ (ให้เปลี่ยนเป็น getUrlDecoder()), หรือ input มีการตัดบรรทัดทุก 76 อักขระ (ให้เปลี่ยนเป็น getMimeDecoder()) ตรวจสอบ raw byte เสมอหาก error message ไม่ชัดเจน

Java 8+
String raw = "c2VydmVyLWNvbmZpZw==\n"; // trailing newline

// Option 1: trim whitespace
byte[] decoded = Base64.getDecoder().decode(raw.strip());

// Option 2: use MIME decoder which ignores whitespace
byte[] decoded2 = Base64.getMimeDecoder().decode(raw);

จะถอดรหัส Base64 ใน Java โดยไม่ใช้ java.util.Base64 ได้ไหม?

ได้ แต่ไม่มีเหตุผลดีพอสำหรับ Java 8+ ก่อน Java 8 นักพัฒนาใช้ sun.misc.BASE64Decoder (ภายใน ถูกลบใน Java 9+), javax.xml.bind.DatatypeConverter.parseBase64Binary() (ถูกลบใน Java 11) หรือ Apache Commons Codec ทั้งสามแบบล้าสมัยหรือต้องการ dependency เพิ่มเติม ควรใช้ java.util.Base64 เพราะเร็วกว่า มาพร้อม JDK และรองรับทั้งสามรูปแบบ (basic, URL-safe, MIME)

เครื่องมือที่เกี่ยวข้อง

  • Base64 Encoder — เข้ารหัสข้อความหรือ binary data เป็น Base64 ในเบราว์เซอร์ สะดวกสำหรับสร้าง test fixture เพื่อวางใน Java unit test
  • JWT Decoder — แยกและถอดรหัสทั้งสาม segment ของ JWT พร้อมตรวจสอบ payload แต่ละ field — เร็วกว่าการเขียน Java class เมื่อต้องการอ่าน token
  • URL Decoder — ถอดรหัส URL-encoded string แบบ percent-decode มีประโยชน์เมื่อ API response รวม Base64url data กับ query parameter ที่ percent-encode ไว้
  • JSON Formatter — หลังถอดรหัส Base64 JWT payload หรือ API config ให้วาง JSON ที่นี่ เพื่อ pretty-print และตรวจสอบโครงสร้าง
มีให้ในภาษาอื่นด้วย:JavaScriptPythonGoC#
PN
Pavel NovakBackend Engineer

Pavel is a backend engineer with deep roots in the JVM ecosystem, working primarily with Java and Kotlin. He has extensive experience building data-intensive services and integrating third-party APIs at scale. He writes about modern Java features, the Jackson ecosystem, serialisation patterns, and practical approaches to keeping large codebases maintainable.

AO
Aisha Oseiผู้ตรวจสอบทางเทคนิค

Aisha is a Java engineer specialising in application security, Spring Security, and API design. She has worked on identity and access management systems, OAuth 2.0 integrations, and microservice security at scale. She writes about secure Java coding practices, token validation, cryptographic utilities, and the Spring ecosystem from a security-first perspective.