현대 애플리케이션에서는 비정형 데이터(예: 이미지, 문서, 동영상 파일 등)의 암호화가 필수적입니다. 특히, 개인정보 보호 및 데이터 유출 방지를 위해 암호화 기술을 활용하는 사례가 많습니다. 이번 글에서는 자바 환경, 특히 스프링 부트에서 비정형 데이터를 암호화하기 위한 주요 라이브러리와 이를 활용한 구현 예제를 소개하겠습니다.
1. 비정형 데이터 암호화를 위한 라이브러리 소개
1.1 Jasypt (Java Simplified Encryption)
• 특징: 설정 기반으로 쉽게 암호화를 구현할 수 있는 자바용 암호화 라이브러리입니다.
• 장점: 비밀번호, 설정 파일 및 일반 데이터를 손쉽게 암호화할 수 있도록 설계되었습니다.
• 제약: 복잡한 데이터 처리보다는 간단한 암호화 작업에 적합합니다.
1.2 Java 표준 암호화 라이브러리 (javax.crypto)
• 특징: 자바 표준 라이브러리로 기본적인 암호화 기능을 제공합니다.
• 장점: 추가적인 의존성 없이 AES, DES, RSA와 같은 알고리즘을 사용할 수 있습니다.
• 제약: 상대적으로 사용이 복잡하며, 반복적으로 코드를 작성해야 합니다.
1.3 Bouncy Castle
• 특징: 오픈소스 암호화 라이브러리로, 자바 환경에서 다양한 암호화 알고리즘을 지원합니다.
• 장점: 강력한 암호화 알고리즘(AES, SEED, Blowfish 등)을 활용할 수 있으며 확장성이 뛰어납니다.
• 제약: 설정과 구현 과정이 다소 복잡할 수 있습니다.
이번 글에서는 Bouncy Castle을 사용하여 스프링 부트에서 비정형 데이터 암호화를 구현하는 방법을 구체적으로 살펴보겠습니다.
2. Bouncy Castle을 활용한 스프링 부트 파일 암호화 구현
2.1 프로젝트 설정
먼저 Bouncy Castle을 활용하기 위해 의존성을 추가합니다.
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.76</version> <!– 최신 버전을 확인 후 사용 –>
</dependency>
2.2 암호화 및 복호화 유틸리티 클래스 작성
Bouncy Castle을 활용한 AES 기반 파일 암호화 및 복호화 유틸리티를 작성합니다.
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.Security;
public class FileEncryptionUtil {
static {
// Bouncy Castle 프로바이더 등록
Security.addProvider(new BouncyCastleProvider());
}
private static final String ALGORITHM = “AES”;
private static final int KEY_SIZE = 256;
// 키 생성
public static SecretKey generateKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM, “BC”);
keyGen.init(KEY_SIZE);
return keyGen.generateKey();
}
// 파일 암호화
public static void encryptFile(File inputFile, File outputFile, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM, “BC”);
cipher.init(Cipher.ENCRYPT_MODE, key);
try (FileInputStream fis = new FileInputStream(inputFile);
FileOutputStream fos = new FileOutputStream(outputFile)) {
byte[] inputBytes = fis.readAllBytes();
byte[] outputBytes = cipher.doFinal(inputBytes);
fos.write(outputBytes);
}
}
// 파일 복호화
public static void decryptFile(File inputFile, File outputFile, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM, “BC”);
cipher.init(Cipher.DECRYPT_MODE, key);
try (FileInputStream fis = new FileInputStream(inputFile);
FileOutputStream fos = new FileOutputStream(outputFile)) {
byte[] inputBytes = fis.readAllBytes();
byte[] outputBytes = cipher.doFinal(inputBytes);
fos.write(outputBytes);
}
}
}
2.3 스프링 부트 서비스 클래스 작성
위의 유틸리티를 스프링 부트에서 활용하도록 서비스 계층을 작성합니다.
import org.springframework.stereotype.Service;
import javax.crypto.SecretKey;
import java.io.File;
@Service
public class FileEncryptionService {
private final SecretKey secretKey;
public FileEncryptionService() throws Exception {
// 애플리케이션 시작 시 고정 키 생성
this.secretKey = FileEncryptionUtil.generateKey();
}
public void encryptFile(String inputPath, String outputPath) throws Exception {
File inputFile = new File(inputPath);
File outputFile = new File(outputPath);
FileEncryptionUtil.encryptFile(inputFile, outputFile, secretKey);
}
public void decryptFile(String inputPath, String outputPath) throws Exception {
File inputFile = new File(inputPath);
File outputFile = new File(outputPath);
FileEncryptionUtil.decryptFile(inputFile, outputFile, secretKey);
}
}
2.4 컨트롤러 작성
파일 업로드 및 암호화 요청을 처리할 컨트롤러를 작성합니다.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
@RestController
@RequestMapping(“/file”)
public class FileEncryptionController {
@Autowired
private FileEncryptionService fileEncryptionService;
@PostMapping(“/encrypt”)
public String encryptFile(@RequestParam(“file”) MultipartFile file) throws Exception {
Path tempFile = Files.createTempFile(“input”, file.getOriginalFilename());
Files.copy(file.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
String encryptedPath = tempFile.getParent().toString() + “/encrypted_” + file.getOriginalFilename();
fileEncryptionService.encryptFile(tempFile.toString(), encryptedPath);
return “Encrypted file saved at: ” + encryptedPath;
}
@PostMapping(“/decrypt”)
public String decryptFile(@RequestParam(“file”) MultipartFile file) throws Exception {
Path tempFile = Files.createTempFile(“encrypted”, file.getOriginalFilename());
Files.copy(file.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
String decryptedPath = tempFile.getParent().toString() + “/decrypted_” + file.getOriginalFilename();
fileEncryptionService.decryptFile(tempFile.toString(), decryptedPath);
return “Decrypted file saved at: ” + decryptedPath;
}
}
2.5 테스트
스프링 부트를 실행한 후, Postman 또는 cURL을 사용하여 파일을 업로드하고 암호화/복호화 결과를 확인합니다.
파일 암호화/복호화 컨트롤러 사용법
위에서 작성한 FileEncryptionController를 사용하여 파일을 암호화하고 복호화하는 방법을 설명하겠습니다. REST API 형태로 구현된 컨트롤러이므로, 파일 업로드를 통해 암호화 및 복호화 요청을 보낼 수 있습니다.
1. 암호화 요청 (/file/encrypt)
API 개요
• URL: /file/encrypt
• HTTP 메서드: POST
• 요청 파라미터:
• file: 업로드할 파일 (필수)
• 응답: 암호화된 파일 경로
예시: Postman을 활용한 테스트
1. Postman 실행 후, POST 요청을 생성합니다.
2. URL에 http://localhost:8080/file/encrypt를 입력합니다.
3. Body 탭으로 이동 후, form-data 옵션을 선택합니다.
4. Key에 file, Value에 업로드할 파일을 추가합니다.
5. Send 버튼을 클릭하면 암호화된 파일 경로가 반환됩니다.
응답 예시:
{
“message”: “Encrypted file saved at: /tmp/encrypted_example.txt”
}
예시: cURL을 활용한 테스트
터미널에서 다음 명령어를 실행합니다.
curl -X POST -F “file=@path_to_your_file/example.txt” http://localhost:8080/file/encrypt
응답 예시:
Encrypted file saved at: /tmp/encrypted_example.txt
2. 복호화 요청 (/file/decrypt)
API 개요
• URL: /file/decrypt
• HTTP 메서드: POST
• 요청 파라미터:
• file: 암호화된 파일 (필수)
• 응답: 복호화된 파일 경로
예시: Postman을 활용한 테스트
1. Postman 실행 후, POST 요청을 생성합니다.
2. URL에 http://localhost:8080/file/decrypt를 입력합니다.
3. Body 탭으로 이동 후, form-data 옵션을 선택합니다.
4. Key에 file, Value에 암호화된 파일을 추가합니다.
5. Send 버튼을 클릭하면 복호화된 파일 경로가 반환됩니다.
응답 예시:
{
“message”: “Decrypted file saved at: /tmp/decrypted_example.txt”
}
예시: cURL을 활용한 테스트
터미널에서 다음 명령어를 실행합니다.
curl -X POST -F “file=@path_to_your_file/encrypted_example.txt” http://localhost:8080/file/decrypt
응답 예시:
Decrypted file saved at: /tmp/decrypted_example.txt
3. 실행 결과 확인
암호화된 파일 확인
/file/encrypt API를 호출한 후, 반환된 경로(/tmp/encrypted_example.txt)에 파일이 저장됩니다. 이 파일은 사람이 읽을 수 없는 암호화된 데이터로 저장됩니다.
복호화된 파일 확인
/file/decrypt API를 호출한 후, 반환된 경로(/tmp/decrypted_example.txt)에 원본 파일 내용이 복원된 형태로 저장됩니다.
4. 주의사항
1. 파일 크기 제한: 업로드할 파일 크기를 설정하려면 application.properties 또는 application.yml에 다음 옵션을 추가하세요.
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
2. 키 관리: 현재 코드는 키를 메모리에 저장하지만, 실제 운영 환경에서는 키를 안전한 저장소(e.g., KeyStore, AWS KMS)에 저장하는 것이 권장됩니다.
3. 임시 파일 경로: /tmp 경로를 사용하도록 설정했으나, 운영 환경에 맞게 변경하세요.
위의 설명을 따라 실행하면 암호화/복호화 기능을 정상적으로 사용할 수 있습니다. 추가적인 요청이 있으면 말씀해주세요!
3. 마무리
이번 글에서는 Bouncy Castle을 활용하여 스프링 부트에서 비정형 데이터를 암호화하는 방법을 살펴보았습니다. Bouncy Castle은 강력한 암호화 알고리즘과 높은 확장성을 제공하여 파일 보안이 중요한 애플리케이션에 적합합니다.
추가적으로, 키 관리 및 보안 강화를 위해 키 저장소(KeyStore) 또는 클라우드 기반 보안 솔루션을 함께 활용하는 것을 권장합니다.