데이터 보호가 필수적인 시대에서 암호화는 더 이상 선택이 아닌 필수 기술입니다. 특히 비정형 데이터(이미지, 문서, 동영상 등)를 안전하게 관리하기 위해서는 강력한 암호화 솔루션이 필요합니다. 이번 글에서는 Bouncy Castle 라이브러리를 활용해 파일 암호화를 구현하고, 대용량 파일 처리를 포함한 실무적인 접근법을 소개합니다.
1. Bouncy Castle의 특징과 장점
주요 특징
• 다양한 암호화 알고리즘 지원: AES, RSA, SEED, Blowfish 등 광범위한 알고리즘 지원.
• 높은 보안성: 최신 보안 표준 준수 및 지속적인 업데이트.
• 확장성: 다양한 환경에서 간단히 적용 가능.
장점
• 오픈소스 기반으로 무료 제공.
• JCA(Java Cryptography Architecture) 호환.
• AES-256 등 강력한 암호화 알고리즘 지원.
2. 프로젝트 설정
Bouncy Castle을 사용하기 위해 의존성을 추가합니다.
Maven
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.77</version>
</dependency>
Gradle
implementation ‘org.bouncycastle:bcprov-jdk18on:1.77’
3. Bouncy Castle 기본 설정
Bouncy Castle 라이브러리를 사용하려면 먼저 Java의 보안 프로바이더로 등록해야 합니다.
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.Security;
public class BouncyCastleSetup {
static {
// Bouncy Castle 보안 프로바이더 등록
Security.addProvider(new BouncyCastleProvider());
}
}
이 코드는 애플리케이션 시작 시 한 번만 실행되며, 이후 암호화 작업에서 자동으로 Bouncy Castle의 알고리즘을 사용할 수 있습니다.
4. AES 파일 암호화 구현
4.1 AES 암호화와 복호화
아래 코드는 AES/CBC/PKCS7Padding 모드를 사용하여 파일 데이터를 암호화 및 복호화합니다.
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.security.SecureRandom;
public class FileEncryption {
// AES 암호화
public byte[] encryptFile(byte[] fileData, SecretKey key) throws Exception {
// 초기화 벡터(IV) 생성
byte[] iv = new byte[16];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// Cipher 초기화
Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS7Padding”, “BC”);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
// 암호화 수행
byte[] encrypted = cipher.doFinal(fileData);
// IV와 암호화 데이터 결합
byte[] result = new byte[iv.length + encrypted.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(encrypted, 0, result, iv.length, encrypted.length);
return result;
}
// AES 복호화
public byte[] decryptFile(byte[] encryptedData, SecretKey key) throws Exception {
// IV 추출
byte[] iv = new byte[16];
System.arraycopy(encryptedData, 0, iv, 0, iv.length);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// 암호화된 데이터 추출
byte[] encrypted = new byte[encryptedData.length – 16];
System.arraycopy(encryptedData, 16, encrypted, 0, encrypted.length);
// Cipher 초기화
Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS7Padding”, “BC”);
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
// 복호화 수행
return cipher.doFinal(encrypted);
}
}
4.2 대용량 파일 암호화 및 복호화
대용량 파일을 처리하려면 스트림 기반으로 데이터를 읽고 쓰는 방식이 필요합니다. 다음 코드는 대용량 파일 암호화를 처리하는 방법입니다.
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.SecureRandom;
public class LargeFileEncryption {
// 대용량 파일 암호화
public void encryptLargeFile(File inputFile, File outputFile, SecretKey key) throws Exception {
// IV 생성
byte[] iv = new byte[16];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// Cipher 초기화
Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS7Padding”, “BC”);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
try (FileInputStream in = new FileInputStream(inputFile);
FileOutputStream out = new FileOutputStream(outputFile)) {
// IV를 파일의 시작 부분에 기록
out.write(iv);
// 버퍼 크기 설정
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
byte[] output = cipher.update(buffer, 0, bytesRead);
if (output != null) {
out.write(output);
}
}
// 최종 블록 처리
byte[] finalOutput = cipher.doFinal();
if (finalOutput != null) {
out.write(finalOutput);
}
}
}
// 대용량 파일 복호화
public void decryptLargeFile(File inputFile, File outputFile, SecretKey key) throws Exception {
try (FileInputStream in = new FileInputStream(inputFile);
FileOutputStream out = new FileOutputStream(outputFile)) {
// IV 읽기
byte[] iv = new byte[16];
in.read(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// Cipher 초기화
Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS7Padding”, “BC”);
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
// 버퍼 크기 설정
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
byte[] output = cipher.update(buffer, 0, bytesRead);
if (output != null) {
out.write(output);
}
}
// 최종 블록 처리
byte[] finalOutput = cipher.doFinal();
if (finalOutput != null) {
out.write(finalOutput);
}
}
}
}
5. 보안 고려사항
1. 키 관리
• 키는 안전한 환경에서 생성하고 저장해야 합니다.
• Java KeyStore, AWS KMS와 같은 솔루션을 활용하는 것이 권장됩니다.
KeyGenerator keyGen = KeyGenerator.getInstance(“AES”, “BC”);
keyGen.init(256); // AES-256 사용
SecretKey key = keyGen.generateKey();
2. 초기화 벡터(IV)
• 암호화 작업마다 새로운 IV를 생성해야 합니다.
• IV는 암호화된 데이터와 함께 저장하거나 별도로 전송해야 복호화가 가능합니다.
3. 암호화 알고리즘 선택
• AES-256/CBC와 같이 강력한 알고리즘과 보안 모드를 사용하세요.
6. 요약
이번 글에서는 Bouncy Castle 라이브러리를 활용한 파일 암호화 및 복호화 방법을 살펴보았습니다. 특히, 대용량 파일 처리와 보안 요소를 고려한 실무적인 접근법을 제안했습니다.
주요 포인트:
• Bouncy Castle의 장점: 다양한 알고리즘 지원과 높은 보안성.
• AES/CBC 모드 활용: 강력한 암호화와 복호화 구현.
• 대용량 파일 처리: 스트림 기반 처리를 통해 메모리 효율성 향상.