문자열 "hello" → Base64
aGVsbG8=
다시 디코딩하면 원래대로 복원
평문 + 키 = 암호문
암호문 + 같은 키 = 평문
AES는 "블록 암호"라서 단독으로 쓰면 불안전하기 때문에 반드시 모드가 필요함.
대표적인 모드:
AES-GCM은 현대 시스템(HTTP/HTTP2, TLS1.3 등)에서 가장 많이 쓰이는 암호화 방식.
GCM 암호화 입력:
산출물:
AES 암호화 결과는 바이너리(byte[])
콘솔이나 JSON, 네트워크로 보내기엔 부적합함.
그래서:
이런 형태가 됨:
Base64(IV) : Base64(암호문)
복호화할 때는 반대로 진행.
| 모드 | 특징 |
| ECB | 절대 사용 금지 (패턴 노출) |
| CBC | 안전하지만 속도 느림, 인증 별도 필요 |
| GCM | 가장 빠르고 안전, 인증 + 암호화 한 번에 해결 |
✔ 현대 개발에서는 "AES/GCM/NoPadding"이 표준
src
└─ main
└─ java
└─ org.example
├─ Main.java
├─ controller
│ └─ CryptoController.java
└─ service
└─ 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;
}
}
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("잘못된 입력입니다.");
}
}
}
}
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=
| POST요청과 GET요청의 차이점 (0) | 2025.11.21 |
|---|---|
| 로컬PC에서 logback.xml 파일 직접 생성해서 설정해보기 (0) | 2025.11.06 |
| Intellij 인텔리제이 공부하기 - pom.xml 파일 역할, 사용 방법 (0) | 2025.11.06 |
| APP api 연동방식에서 STM방식, App방식 각각 개념과 차이점은? (0) | 2025.11.05 |
| PUID란? (5) | 2025.08.16 |