상세 컨텐츠

본문 제목

Base64 인코딩, 디코딩, AES-GCM 모드란? (예제 코드포함)

IT

by JR 2025. 11. 18. 14:24

본문

300x250
반응형

 

 

🧠 1. Base64 인코딩 / 디코딩 개념

✔ Base64란?

  • Binary → Text(문자열)로 변환하는 인코딩 방식
  • 암호화가 아님
  • 정보를 안전하게 읽지 못하게 하는 것이 아니라,
    바이너리 데이터를 문자로만 표현해 전송 가능한 형태로 만드는 방식

✔ 왜 쓰는가?

  1. 이메일, HTTP Body, URL 등은 ASCII 문자만 안전하게 전송 가능
  2. 이미지/파일 같은 binary 데이터를 그대로 보내면 깨짐
  3. 그래서 데이터를 Base64로 바꿔서 전송함

✔ Base64의 대표 특징

  • 원본보다 약 33% 용량 증가
  • 정보 보호 목적이 아님 → 단순 변환
  • 쉽게 다시 복원됨 (디코딩)

✔ 간단한 예

문자열 "hello" → Base64

aGVsbG8=

 

다시 디코딩하면 원래대로 복원


🔐 2. AES256 대칭키 암호화 개념

✔ AES란?

  • Advanced Encryption Standard
  • 미국 국립표준기술연구소(NIST) 표준 알고리즘
  • 현재 전 세계에서 가장 널리 사용되는 대칭키 암호화 방식

✔ 대칭키 암호화란?

  • 암호화와 복호화에 동일한 키 1개를 사용
  • 비대칭키(RSA)와 대비됨 (RSA는 공개키/개인키 2개 사용)
평문 + 키 = 암호문
암호문 + 같은 키 = 평문

✔ AES256의 의미

  • "256"은 키 길이(256bit)
  • 키가 길수록 brute-force 공격이 어려움
  • AES-128도 충분히 안전하지만, 보안 높이면 AES-256 사용

✔ AES + 암호 모드 (Mode)

AES는 "블록 암호"라서 단독으로 쓰면 불안전하기 때문에 반드시 모드가 필요함.

대표적인 모드:

  • CBC(전통적인 방식)
  • GCM ← 요즘 가장 추천되는 모드

🛡 3. AES-GCM 모드

AES-GCM은 현대 시스템(HTTP/HTTP2, TLS1.3 등)에서 가장 많이 쓰이는 암호화 방식.

✔ GCM이 안전한 이유

  1. **암호화 + 인증(무결성 검사)**를 동시에 제공
    → 전송 중 데이터가 조작되면 자동으로 탐지됨
  2. 빠르고 병렬 처리에 효율적
  3. 최신 표준에서 권장됨 (TLS 1.3 기본)

✔ GCM의 구조

GCM 암호화 입력:

  • 평문
  • 256bit AES 키
  • IV(Initial Vector, 초기화 벡터) ← 매우 중요!
  • 인증 태그(Integrity Tag)

산출물:

  • 암호문(ciphertext)
  • 인증 태그(tag)

✔ IV(초기화 벡터)의 개념

  • GCM에서는 12바이트(96bit) IV 권장
  • 암호화할 때 매번 랜덤하게 생성해야 함
  • IV가 동일하면 큰 보안 취약점 발생
  • 하지만 IV는 비밀이 아님 → 암호문과 함께 전송해도 됨

🔁 4. Base64 + AES 함께 쓰는 이유

AES 암호화 결과는 바이너리(byte[])
콘솔이나 JSON, 네트워크로 보내기엔 부적합함.

그래서:

AES 암호문 → Base64 변환 → 전송/저장

이런 형태가 됨:

Base64(IV) : Base64(암호문)

 

복호화할 때는 반대로 진행.


📌 전체 흐름 요약

🔒 암호화 과정

  1. 문자열 입력
  2. AES-256 키 준비
  3. IV 생성 (랜덤)
  4. GCM 모드로 암호화
  5. 암호문 + IV → Base64로 포장
  6. 출력

🔓 복호화 과정

  1. Base64 문자열 입력
  2. IV와 암호문 분리
  3. Base64 디코딩
  4. AES-256 GCM 복호화
  5. 평문 복원

🧩 왜 GCM 모드를 쓰나요?

모드 특징
ECB 절대 사용 금지 (패턴 노출)
CBC 안전하지만 속도 느림, 인증 별도 필요
GCM 가장 빠르고 안전, 인증 + 암호화 한 번에 해결

 

✔ 현대 개발에서는 "AES/GCM/NoPadding"이 표준


🎯 한 줄 요약

  • Base64 = 데이터를 문자로 변환하는 것(암호화 아님)
  • AES256 = 강력한 대칭키 암호화 방식
  • GCM 모드 = 빠르고 안전하며 데이터 위조도 탐지
  • IV = 매번 랜덤 생성해야 안전
  • 암호문+IV는 Base64로 변환해 전송하는 것이 일반적

 

 

📌 예제 주제: 문자열 암호화/복호화 유틸 프로그램

기능

  1. 문자열 Base64 인코딩
  2. Base64 디코딩
  3. AES-256 대칭키 암호화
  4. AES-256 복호화

📁 프로젝트 구조

src
 └─ main
     └─ java
         └─ org.example
             ├─ Main.java
             ├─ controller
             │     └─ CryptoController.java
             └─ service
                   └─ CryptoService.java

✅ 1. CryptoService.java (암호화/복호화 로직)

src/main/java/org/example/service/CryptoService.java

package org.example.service;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.util.Base64;

public class CryptoService {

    // AES-GCM 모드에서 사용되는 초기화 벡터(IV) 길이(12byte = 96bit 권장)
    private static final int IV_SIZE = 12;

    // GCM 모드 인증 태그 길이(128bit 권장)
    private static final int TAG_SIZE = 128;

    /**
     * Base64 인코딩 (문자열 → Base64 문자열)
     */
    public String encodeBase64(String input) {
        return Base64.getEncoder().encodeToString(input.getBytes());
    }

    /**
     * Base64 디코딩 (Base64 문자열 → 원본 문자열)
     */
    public String decodeBase64(String input) {
        byte[] decoded = Base64.getDecoder().decode(input);
        return new String(decoded);
    }

    /**
     * AES256 GCM 암호화
     */
    public String encryptAES(String plainText, SecretKey key, byte[] iv) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

        // 암호화 모드로 초기화, GCMParameterSpec 사용
        cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(TAG_SIZE, iv));

        byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());

        // (암호문 + IV)를 Base64로 묶어서 전달
        return Base64.getEncoder().encodeToString(iv) + ":" +
                Base64.getEncoder().encodeToString(encryptedBytes);
    }

    /**
     * AES256 GCM 복호화
     */
    public String decryptAES(String encryptedText, SecretKey key) throws Exception {

        // 암호문 포맷 = "IV:암호문"
        String[] parts = encryptedText.split(":");

        byte[] iv = Base64.getDecoder().decode(parts[0]);
        byte[] cipherBytes = Base64.getDecoder().decode(parts[1]);

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

        cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(TAG_SIZE, iv));

        byte[] decryptedBytes = cipher.doFinal(cipherBytes);

        return new String(decryptedBytes);
    }

    /**
     * AES256 키 생성
     */
    public SecretKey generateAESKey() throws Exception {
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(256); // AES-256
        return keyGen.generateKey();
    }

    /**
     * 임의의 IV 생성
     */
    public byte[] generateIV() {
        byte[] iv = new byte[IV_SIZE];
        new java.security.SecureRandom().nextBytes(iv);
        return iv;
    }
}

✅ 2. CryptoController.java (사용자 입력 처리)

src/main/java/org/example/controller/CryptoController.java

package org.example.controller;

import org.example.service.CryptoService;

import javax.crypto.SecretKey;
import java.util.Scanner;

public class CryptoController {

    private final CryptoService service = new CryptoService();
    private SecretKey aesKey; // 프로그램 실행 중 유지될 AES 키

    public CryptoController() {
        try {
            aesKey = service.generateAESKey();
        } catch (Exception e) {
            System.out.println("AES 키 생성 실패: " + e.getMessage());
        }
    }

    // 프로그램 실행
    public void start() {
        Scanner sc = new Scanner(System.in);
        int choice;

        while (true) {
            System.out.println("\n========== 암호화 프로그램 ==========");
            System.out.println("1. Base64 인코딩");
            System.out.println("2. Base64 디코딩");
            System.out.println("3. AES256 암호화");
            System.out.println("4. AES256 복호화");
            System.out.println("0. 종료");
            System.out.print("선택: ");

            choice = sc.nextInt();
            sc.nextLine(); // 개행 제거

            switch (choice) {
                case 1:
                    System.out.print("인코딩할 문자열: ");
                    String encodeInput = sc.nextLine();
                    System.out.println("▶ 결과: " + service.encodeBase64(encodeInput));
                    break;

                case 2:
                    System.out.print("디코딩할 Base64 문자열: ");
                    String decodeInput = sc.nextLine();
                    System.out.println("▶ 결과: " + service.decodeBase64(decodeInput));
                    break;

                case 3:
                    try {
                        System.out.print("AES로 암호화할 문자열: ");
                        String text = sc.nextLine();

                        byte[] iv = service.generateIV();
                        String encrypted = service.encryptAES(text, aesKey, iv);

                        System.out.println("▶ 암호문: " + encrypted);
                    } catch (Exception e) {
                        System.out.println("암호화 오류: " + e.getMessage());
                    }
                    break;

                case 4:
                    try {
                        System.out.print("복호화할 암호문 입력: ");
                        String encryptedText = sc.nextLine();

                        String decrypted = service.decryptAES(encryptedText, aesKey);

                        System.out.println("▶ 복호화 결과: " + decrypted);
                    } catch (Exception e) {
                        System.out.println("복호화 오류: " + e.getMessage());
                    }
                    break;

                case 0:
                    System.out.println("프로그램 종료");
                    return;

                default:
                    System.out.println("잘못된 입력입니다.");
            }
        }
    }
}

✅ 3. Main.java

src/main/java/org/example/Main.java

package org.example;

import org.example.controller.CryptoController;

public class Main {
    public static void main(String[] args) {
        new CryptoController().start();
    }
}

🎉 실행 화면 예시

========== 암호화 프로그램 ==========
1. Base64 인코딩
2. Base64 디코딩
3. AES256 암호화
4. AES256 복호화
0. 종료
선택: 1
인코딩할 문자열: hello
▶ 결과: aGVsbG8=

 

300x250
반응형

관련글 더보기