728x90
Spring 애플리케이션에서 민감한 데이터를 보호하기 위해 RSA와 AES 암호화를 구현하는 방법을 알아보겠습니다.
RSA는 비대칭 암호화 방식으로 키 교환에 주로 사용되고, AES는 대칭 암호화 방식으로 대용량 데이터 암호화에 효율적입니다. 두 방식을 조합하여 사용하는 하이브리드 암호화 방법도 함께 살펴보겠습니다.
목차
1. 암호화 알고리즘 개요
1.1 RSA (Rivest-Shamir-Adleman)
RSA는 공개키 암호화 알고리즘으로, 공개키로 암호화하고 개인키로 복호화합니다.
- 비대칭 암호화: 공개키와 개인키 쌍 사용
- 키 교환: 안전한 키 교환에 적합
- 성능: 대용량 데이터에는 느림 (일반적으로 245바이트 이하 권장)
- 용도: 디지털 서명, 키 교환, 소량 데이터 암호화
1.2 AES (Advanced Encryption Standard)
AES는 대칭키 암호화 알고리즘으로, 동일한 키로 암호화와 복호화를 수행합니다.
- 대칭 암호화: 동일한 키로 암호화/복호화
- 성능: 빠른 암호화/복호화 속도
- 키 크기: 128, 192, 256비트 지원
- 용도: 대용량 데이터 암호화, 파일 암호화
1.3 RSA vs AES 비교
| 항목 | RSA | AES |
|---|---|---|
| 암호화 방식 | 비대칭 (공개키/개인키) | 대칭 (동일 키) |
| 성능 | 느림 (소량 데이터) | 빠름 (대용량 데이터) |
| 키 관리 | 복잡 (키 쌍 필요) | 간단 (단일 키) |
| 주요 용도 | 키 교환, 서명 | 데이터 암호화 |
💡 팁: 실제 프로덕션 환경에서는 하이브리드 암호화를 사용합니다. RSA로 AES 키를 암호화하여 전송하고, AES로 실제 데이터를 암호화합니다. 이렇게 하면 RSA의 안전성과 AES의 성능을 모두 활용할 수 있습니다.
2. 프로젝트 설정
2.1 build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.0'
id 'io.spring.dependency-management' version '1.1.0'
}
dependencies {
// Spring Boot
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// QueryDSL
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
implementation 'com.querydsl:querydsl-apt:5.0.0:jakarta'
// 암호화 라이브러리 (Java 기본 제공, 별도 의존성 불필요)
// java.security.* 패키지 사용
// Base64 인코딩/디코딩 (Java 8+ 기본 제공)
// java.util.Base64 사용
// Lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// QueryDSL Annotation Processor
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
}
⚠️ 중요: Java는 암호화 기능을 기본 제공하므로 별도의 외부 라이브러리가 필요하지 않습니다.
java.security와 javax.crypto 패키지를 사용합니다.3. RSA 암호화/복호화 구현
3.1 RSAKeyGenerator.java - 키 쌍 생성
package com.example.crypto;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Base64;
public class RSAKeyGenerator {
/**
* RSA 키 쌍 생성 (2048비트)
*/
public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048); // 2048비트 키 크기
return keyPairGenerator.generateKeyPair();
}
/**
* 공개키를 Base64 문자열로 변환
*/
public static String publicKeyToString(RSAPublicKey publicKey) {
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
}
/**
* 개인키를 Base64 문자열로 변환
*/
public static String privateKeyToString(RSAPrivateKey privateKey) {
return Base64.getEncoder().encodeToString(privateKey.getEncoded());
}
/**
* 키 생성 및 출력 예제
*/
public static void main(String[] args) throws Exception {
KeyPair keyPair = generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
System.out.println("=== RSA 키 쌍 생성 ===");
System.out.println("공개키:");
System.out.println(publicKeyToString(publicKey));
System.out.println("\n개인키:");
System.out.println(privateKeyToString(privateKey));
}
}
3.2 RSAUtil.java - 암호화/복호화 유틸리티
package com.example.crypto;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class RSAUtil {
private static final String ALGORITHM = "RSA";
private static final String TRANSFORMATION = "RSA/ECB/PKCS1Padding";
/**
* Base64 문자열을 공개키로 변환
*/
public static PublicKey getPublicKey(String base64PublicKey) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(base64PublicKey);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
return keyFactory.generatePublic(spec);
}
/**
* Base64 문자열을 개인키로 변환
*/
public static PrivateKey getPrivateKey(String base64PrivateKey) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(base64PrivateKey);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
return keyFactory.generatePrivate(spec);
}
/**
* 공개키로 암호화
*/
public static String encrypt(String plainText, String publicKeyStr) throws Exception {
PublicKey publicKey = getPublicKey(publicKeyStr);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] plainTextBytes = plainText.getBytes(StandardCharsets.UTF_8);
// RSA는 블록 단위로 암호화하므로 데이터를 분할해야 함
// 2048비트 키의 경우 최대 245바이트까지 암호화 가능
int maxBlockSize = 245;
StringBuilder encrypted = new StringBuilder();
for (int i = 0; i < plainTextBytes.length; i += maxBlockSize) {
int endIndex = Math.min(i + maxBlockSize, plainTextBytes.length);
byte[] block = new byte[endIndex - i];
System.arraycopy(plainTextBytes, i, block, 0, block.length);
byte[] encryptedBlock = cipher.doFinal(block);
encrypted.append(Base64.getEncoder().encodeToString(encryptedBlock));
if (endIndex < plainTextBytes.length) {
encrypted.append(":");
}
}
return encrypted.toString();
}
/**
* 개인키로 복호화
*/
public static String decrypt(String encryptedText, String privateKeyStr) throws Exception {
PrivateKey privateKey = getPrivateKey(privateKeyStr);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
String[] blocks = encryptedText.split(":");
StringBuilder decrypted = new StringBuilder();
for (String block : blocks) {
byte[] encryptedBytes = Base64.getDecoder().decode(block);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
decrypted.append(new String(decryptedBytes, StandardCharsets.UTF_8));
}
return decrypted.toString();
}
}
3.3 RSA 사용 예제
package com.example.crypto;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
public class RSAExample {
public static void main(String[] args) throws Exception {
// 1. 키 쌍 생성
KeyPair keyPair = RSAKeyGenerator.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
String publicKeyStr = RSAKeyGenerator.publicKeyToString(publicKey);
String privateKeyStr = RSAKeyGenerator.privateKeyToString(privateKey);
System.out.println("공개키: " + publicKeyStr);
System.out.println("개인키: " + privateKeyStr);
// 2. 암호화할 데이터
String plainText = "안녕하세요! 이것은 암호화 테스트입니다.";
System.out.println("\n원본 텍스트: " + plainText);
// 3. 공개키로 암호화
String encrypted = RSAUtil.encrypt(plainText, publicKeyStr);
System.out.println("암호화된 텍스트: " + encrypted);
// 4. 개인키로 복호화
String decrypted = RSAUtil.decrypt(encrypted, privateKeyStr);
System.out.println("복호화된 텍스트: " + decrypted);
// 5. 검증
System.out.println("\n암호화/복호화 성공: " + plainText.equals(decrypted));
}
}
⚠️ 주의: RSA는 대용량 데이터를 직접 암호화하기에는 비효율적입니다. 2048비트 키의 경우 최대 245바이트까지만 암호화할 수 있습니다. 대용량 데이터는 하이브리드 방식(RSA로 AES 키 암호화)을 사용하세요.
4. AES 암호화/복호화 구현
4.1 AESUtil.java - AES 암호화/복호화 유틸리티
package com.example.crypto;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
public class AESUtil {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private static final int KEY_SIZE = 256; // 256비트 키
private static final int GCM_IV_LENGTH = 12; // GCM IV 길이 (12바이트)
private static final int GCM_TAG_LENGTH = 16; // GCM 태그 길이 (16바이트)
/**
* AES 키 생성 (256비트)
*/
public static SecretKey generateKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
keyGenerator.init(KEY_SIZE);
return keyGenerator.generateKey();
}
/**
* Base64 문자열을 SecretKey로 변환
*/
public static SecretKey getKeyFromString(String keyStr) {
byte[] decodedKey = Base64.getDecoder().decode(keyStr);
return new SecretKeySpec(decodedKey, ALGORITHM);
}
/**
* SecretKey를 Base64 문자열로 변환
*/
public static String keyToString(SecretKey key) {
return Base64.getEncoder().encodeToString(key.getEncoded());
}
/**
* AES로 암호화
*/
public static String encrypt(String plainText, String keyStr) throws Exception {
SecretKey key = getKeyFromString(keyStr);
return encrypt(plainText, key);
}
/**
* AES로 암호화 (SecretKey 사용)
*/
public static String encrypt(String plainText, SecretKey key) throws Exception {
// IV(Initialization Vector) 생성
byte[] iv = new byte[GCM_IV_LENGTH];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
// Cipher 초기화
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
// 암호화 수행
byte[] plainTextBytes = plainText.getBytes(StandardCharsets.UTF_8);
byte[] encryptedBytes = cipher.doFinal(plainTextBytes);
// IV와 암호화된 데이터를 결합하여 Base64로 인코딩
byte[] combined = new byte[iv.length + encryptedBytes.length];
System.arraycopy(iv, 0, combined, 0, iv.length);
System.arraycopy(encryptedBytes, 0, combined, iv.length, encryptedBytes.length);
return Base64.getEncoder().encodeToString(combined);
}
/**
* AES로 복호화
*/
public static String decrypt(String encryptedText, String keyStr) throws Exception {
SecretKey key = getKeyFromString(keyStr);
return decrypt(encryptedText, key);
}
/**
* AES로 복호화 (SecretKey 사용)
*/
public static String decrypt(String encryptedText, SecretKey key) throws Exception {
// Base64 디코딩
byte[] combined = Base64.getDecoder().decode(encryptedText);
// IV 추출
byte[] iv = new byte[GCM_IV_LENGTH];
System.arraycopy(combined, 0, iv, 0, iv.length);
// 암호화된 데이터 추출
byte[] encryptedBytes = new byte[combined.length - GCM_IV_LENGTH];
System.arraycopy(combined, GCM_IV_LENGTH, encryptedBytes, 0, encryptedBytes.length);
// Cipher 초기화
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
// 복호화 수행
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
}
4.2 AES 사용 예제
package com.example.crypto;
import javax.crypto.SecretKey;
public class AESExample {
public static void main(String[] args) throws Exception {
// 1. AES 키 생성
SecretKey key = AESUtil.generateKey();
String keyStr = AESUtil.keyToString(key);
System.out.println("AES 키: " + keyStr);
// 2. 암호화할 데이터
String plainText = "안녕하세요! 이것은 AES 암호화 테스트입니다. 대용량 데이터도 빠르게 암호화할 수 있습니다.";
System.out.println("\n원본 텍스트: " + plainText);
// 3. 암호화
String encrypted = AESUtil.encrypt(plainText, keyStr);
System.out.println("암호화된 텍스트: " + encrypted);
// 4. 복호화
String decrypted = AESUtil.decrypt(encrypted, keyStr);
System.out.println("복호화된 텍스트: " + decrypted);
// 5. 검증
System.out.println("\n암호화/복호화 성공: " + plainText.equals(decrypted));
}
}
💡 팁: AES-GCM 모드는 인증된 암호화(Authenticated Encryption)를 제공하여 데이터 무결성도 보장합니다. IV(Initialization Vector)는 매번 랜덤하게 생성하여 같은 평문도 다른 암호문을 생성하도록 합니다.
5. 하이브리드 암호화 (RSA + AES)
하이브리드 암호화는 RSA의 안전한 키 교환과 AES의 빠른 암호화 성능을 결합한 방식입니다.
5.1 하이브리드 암호화 과정
1. AES 키 생성 (랜덤)
↓
2. AES 키를 RSA 공개키로 암호화
↓
3. 실제 데이터를 AES 키로 암호화
↓
4. 암호화된 AES 키 + 암호화된 데이터 전송
↓
5. 수신 측에서 RSA 개인키로 AES 키 복호화
↓
6. 복호화된 AES 키로 데이터 복호화
5.2 HybridCryptoUtil.java
package com.example.crypto;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class HybridCryptoUtil {
/**
* 하이브리드 암호화 (RSA + AES)
* @param plainText 암호화할 평문
* @param rsaPublicKey RSA 공개키 (Base64)
* @return "암호화된AES키:암호화된데이터" 형식의 문자열
*/
public static String encrypt(String plainText, String rsaPublicKey) throws Exception {
// 1. AES 키 생성
SecretKey aesKey = AESUtil.generateKey();
String aesKeyStr = AESUtil.keyToString(aesKey);
// 2. AES 키를 RSA 공개키로 암호화
String encryptedAESKey = RSAUtil.encrypt(aesKeyStr, rsaPublicKey);
// 3. 실제 데이터를 AES 키로 암호화
String encryptedData = AESUtil.encrypt(plainText, aesKey);
// 4. 암호화된 AES 키와 암호화된 데이터를 결합
return encryptedAESKey + ":" + encryptedData;
}
/**
* 하이브리드 복호화 (RSA + AES)
* @param encryptedText "암호화된AES키:암호화된데이터" 형식의 문자열
* @param rsaPrivateKey RSA 개인키 (Base64)
* @return 복호화된 평문
*/
public static String decrypt(String encryptedText, String rsaPrivateKey) throws Exception {
// 1. 암호화된 AES 키와 암호화된 데이터 분리
String[] parts = encryptedText.split(":", 2);
if (parts.length != 2) {
throw new IllegalArgumentException("Invalid encrypted text format");
}
String encryptedAESKey = parts[0];
String encryptedData = parts[1];
// 2. RSA 개인키로 AES 키 복호화
String decryptedAESKey = RSAUtil.decrypt(encryptedAESKey, rsaPrivateKey);
// 3. 복호화된 AES 키로 데이터 복호화
String decryptedData = AESUtil.decrypt(encryptedData, decryptedAESKey);
return decryptedData;
}
}
5.3 하이브리드 암호화 사용 예제
package com.example.crypto;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
public class HybridCryptoExample {
public static void main(String[] args) throws Exception {
// 1. RSA 키 쌍 생성
KeyPair keyPair = RSAKeyGenerator.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
String publicKeyStr = RSAKeyGenerator.publicKeyToString(publicKey);
String privateKeyStr = RSAKeyGenerator.privateKeyToString(privateKey);
// 2. 암호화할 대용량 데이터
String plainText = "이것은 하이브리드 암호화 테스트입니다. " +
"RSA의 안전성과 AES의 성능을 모두 활용하여 " +
"대용량 데이터를 효율적으로 암호화할 수 있습니다. " +
"실제 프로덕션 환경에서 가장 많이 사용되는 방식입니다.";
System.out.println("원본 텍스트: " + plainText);
System.out.println("원본 길이: " + plainText.length() + " 바이트");
// 3. 하이브리드 암호화
String encrypted = HybridCryptoUtil.encrypt(plainText, publicKeyStr);
System.out.println("\n암호화된 텍스트: " + encrypted);
// 4. 하이브리드 복호화
String decrypted = HybridCryptoUtil.decrypt(encrypted, privateKeyStr);
System.out.println("복호화된 텍스트: " + decrypted);
// 5. 검증
System.out.println("\n암호화/복호화 성공: " + plainText.equals(decrypted));
}
}
6. Spring Service로 통합
6.1 CryptoService.java
package com.example.service;
import com.example.crypto.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.crypto.SecretKey;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
@Slf4j
@Service
public class CryptoService {
@Value("${crypto.rsa.public-key:}")
private String rsaPublicKey;
@Value("${crypto.rsa.private-key:}")
private String rsaPrivateKey;
@Value("${crypto.aes.key:}")
private String aesKey;
/**
* RSA 암호화
*/
public String encryptWithRSA(String plainText) {
try {
return RSAUtil.encrypt(plainText, rsaPublicKey);
} catch (Exception e) {
log.error("RSA 암호화 실패", e);
throw new RuntimeException("RSA encryption failed", e);
}
}
/**
* RSA 복호화
*/
public String decryptWithRSA(String encryptedText) {
try {
return RSAUtil.decrypt(encryptedText, rsaPrivateKey);
} catch (Exception e) {
log.error("RSA 복호화 실패", e);
throw new RuntimeException("RSA decryption failed", e);
}
}
/**
* AES 암호화
*/
public String encryptWithAES(String plainText) {
try {
return AESUtil.encrypt(plainText, aesKey);
} catch (Exception e) {
log.error("AES 암호화 실패", e);
throw new RuntimeException("AES encryption failed", e);
}
}
/**
* AES 복호화
*/
public String decryptWithAES(String encryptedText) {
try {
return AESUtil.decrypt(encryptedText, aesKey);
} catch (Exception e) {
log.error("AES 복호화 실패", e);
throw new RuntimeException("AES decryption failed", e);
}
}
/**
* 하이브리드 암호화 (RSA + AES)
*/
public String encryptHybrid(String plainText) {
try {
return HybridCryptoUtil.encrypt(plainText, rsaPublicKey);
} catch (Exception e) {
log.error("하이브리드 암호화 실패", e);
throw new RuntimeException("Hybrid encryption failed", e);
}
}
/**
* 하이브리드 복호화 (RSA + AES)
*/
public String decryptHybrid(String encryptedText) {
try {
return HybridCryptoUtil.decrypt(encryptedText, rsaPrivateKey);
} catch (Exception e) {
log.error("하이브리드 복호화 실패", e);
throw new RuntimeException("Hybrid decryption failed", e);
}
}
/**
* RSA 키 쌍 생성
*/
public KeyPair generateRSAKeyPair() {
try {
return RSAKeyGenerator.generateKeyPair();
} catch (Exception e) {
log.error("RSA 키 쌍 생성 실패", e);
throw new RuntimeException("RSA key pair generation failed", e);
}
}
/**
* AES 키 생성
*/
public SecretKey generateAESKey() {
try {
return AESUtil.generateKey();
} catch (Exception e) {
log.error("AES 키 생성 실패", e);
throw new RuntimeException("AES key generation failed", e);
}
}
}
6.2 application.yml 설정
crypto:
rsa:
# RSA 공개키 (Base64)
public-key: YOUR_RSA_PUBLIC_KEY_HERE
# RSA 개인키 (Base64) - 절대 공개하지 마세요!
private-key: YOUR_RSA_PRIVATE_KEY_HERE
aes:
# AES 키 (Base64) - 절대 공개하지 마세요!
key: YOUR_AES_KEY_HERE
⚠️ 보안 주의: 실제 운영 환경에서는 키를 환경 변수나 보안 저장소에 저장하고,
application.yml에 직접 저장하지 마세요. Spring Cloud Config, AWS Secrets Manager 등을 사용하세요.7. 실제 사용 예제
7.1 CryptoController.java
package com.example.controller;
import com.example.service.CryptoService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/crypto")
@RequiredArgsConstructor
public class CryptoController {
private final CryptoService cryptoService;
/**
* RSA 암호화
*/
@PostMapping("/rsa/encrypt")
public ResponseEntity<Map<String, String>> encryptRSA(@RequestBody Map<String, String> request) {
String plainText = request.get("text");
String encrypted = cryptoService.encryptWithRSA(plainText);
Map<String, String> response = new HashMap<>();
response.put("encrypted", encrypted);
return ResponseEntity.ok(response);
}
/**
* RSA 복호화
*/
@PostMapping("/rsa/decrypt")
public ResponseEntity<Map<String, String>> decryptRSA(@RequestBody Map<String, String> request) {
String encrypted = request.get("encrypted");
String decrypted = cryptoService.decryptWithRSA(encrypted);
Map<String, String> response = new HashMap<>();
response.put("decrypted", decrypted);
return ResponseEntity.ok(response);
}
/**
* AES 암호화
*/
@PostMapping("/aes/encrypt")
public ResponseEntity<Map<String, String>> encryptAES(@RequestBody Map<String, String> request) {
String plainText = request.get("text");
String encrypted = cryptoService.encryptWithAES(plainText);
Map<String, String> response = new HashMap<>();
response.put("encrypted", encrypted);
return ResponseEntity.ok(response);
}
/**
* AES 복호화
*/
@PostMapping("/aes/decrypt")
public ResponseEntity<Map<String, String>> decryptAES(@RequestBody Map<String, String> request) {
String encrypted = request.get("encrypted");
String decrypted = cryptoService.decryptWithAES(encrypted);
Map<String, String> response = new HashMap<>();
response.put("decrypted", decrypted);
return ResponseEntity.ok(response);
}
/**
* 하이브리드 암호화
*/
@PostMapping("/hybrid/encrypt")
public ResponseEntity<Map<String, String>> encryptHybrid(@RequestBody Map<String, String> request) {
String plainText = request.get("text");
String encrypted = cryptoService.encryptHybrid(plainText);
Map<String, String> response = new HashMap<>();
response.put("encrypted", encrypted);
return ResponseEntity.ok(response);
}
/**
* 하이브리드 복호화
*/
@PostMapping("/hybrid/decrypt")
public ResponseEntity<Map<String, String>> decryptHybrid(@RequestBody Map<String, String> request) {
String encrypted = request.get("encrypted");
String decrypted = cryptoService.decryptHybrid(encrypted);
Map<String, String> response = new HashMap<>();
response.put("decrypted", decrypted);
return ResponseEntity.ok(response);
}
}
7.2 API 사용 예제
RSA 암호화/복호화:
# RSA 암호화
curl -X POST http://localhost:8080/api/crypto/rsa/encrypt \
-H "Content-Type: application/json" \
-d '{"text": "안녕하세요!"}'
# RSA 복호화
curl -X POST http://localhost:8080/api/crypto/rsa/decrypt \
-H "Content-Type: application/json" \
-d '{"encrypted": "암호화된_문자열"}'
AES 암호화/복호화:
# AES 암호화
curl -X POST http://localhost:8080/api/crypto/aes/encrypt \
-H "Content-Type: application/json" \
-d '{"text": "대용량 데이터 암호화 테스트"}'
# AES 복호화
curl -X POST http://localhost:8080/api/crypto/aes/decrypt \
-H "Content-Type: application/json" \
-d '{"encrypted": "암호화된_문자열"}'
하이브리드 암호화/복호화:
# 하이브리드 암호화
curl -X POST http://localhost:8080/api/crypto/hybrid/encrypt \
-H "Content-Type: application/json" \
-d '{"text": "하이브리드 암호화 테스트입니다."}'
# 하이브리드 복호화
curl -X POST http://localhost:8080/api/crypto/hybrid/decrypt \
-H "Content-Type: application/json" \
-d '{"encrypted": "암호화된_문자열"}'
8. 보안 주의사항
8.1 키 관리
- 개인키 보호: RSA 개인키와 AES 키는 절대 공개하지 마세요
- 환경 변수 사용: 키를 코드나 설정 파일에 하드코딩하지 마세요
- 키 로테이션: 정기적으로 키를 교체하세요
- 키 저장소: AWS Secrets Manager, HashiCorp Vault 등 사용
8.2 암호화 모드 선택
| 상황 | 권장 방식 |
|---|---|
| 소량 데이터 (< 245바이트) | RSA |
| 대용량 데이터 | AES 또는 하이브리드 |
| 키 교환이 필요한 경우 | 하이브리드 (RSA + AES) |
| 단일 서버 내부 암호화 | AES |
8.3 추가 보안 고려사항
- HTTPS 사용: 전송 중 데이터 보호를 위해 HTTPS 필수
- Salt 사용: 패스워드 암호화 시 Salt 추가
- IV 관리: AES 사용 시 매번 새로운 IV 생성
- 에러 처리: 암호화 실패 시 민감한 정보 노출 방지
- 로깅: 키나 평문을 로그에 남기지 않기
✅ 핵심 요약:
- RSA는 비대칭 암호화, AES는 대칭 암호화
- RSA는 소량 데이터, AES는 대용량 데이터에 적합
- 하이브리드 암호화는 RSA + AES를 결합한 방식
- Java는 암호화 기능을 기본 제공 (별도 라이브러리 불필요)
- 키 관리는 보안의 핵심 - 절대 공개하지 마세요
- 실제 운영 환경에서는 키를 안전한 저장소에 보관

카카오톡 오픈채팅 링크
https://open.kakao.com/o/seCteX7h
추가로 궁금한 점이 있으시면 댓글로 남겨주세요! 🚀
참고 자료
728x90
'개발 > BACK' 카테고리의 다른 글
| Springboot 스케줄러 개발 예제 @Scheduled (0) | 2025.12.24 |
|---|---|
| Spring 환경에서 DB 접근 최소화 방법 정리 (0) | 2025.12.24 |
| SpringBoot QueryDSL 환경에서 Servlet 구현 예제 (0) | 2025.12.24 |
| SpringBoot 파일 전송 구현하기 (MVC 패턴 + QueryDSL) (0) | 2025.12.24 |
| WEB-INF 정적 리소스 경로 찾기 - 로컬환경과 배포환경의 차이점 (0) | 2025.12.24 |