04 파일명 암호화하여 OS 저장
04 파일명 암호화하여 OS 저장
1. note
- 과금정산플랫폼 노트(2025.11.29.)
2. 요청 내용
- 웹에서 파일 업로드할때 파일을 암호화하여 저장하기.
3. Memo
- 암호화
- 파일을 업로드할떄 암호화하여 저장
- 작업 완료한 파일을 이동할때는 암호화상태로 이동
- 복호화
- 서비스단에서는 기존과 동일하게 화면코드와 파일명 조합으로 작업을 진행함.
- 공통 SFTP 서비스에서 파일을 읽을때 전체 복호화 처리 진행함.
- 별도로 비즈니스로직 내에서 복호화작업을 만들지 않음.
4. 암호화
1. Point
| 개념 | 역할 | 왜 필요한가 | 예시/비유 |
|---|---|---|---|
| AES | 대칭키 암호화 알고리즘 | 같은 키로 암호화·복호화 가능. 속도가 빠르고 표준이라 파일명 보호에 적합 | 자물쇠 + 열쇠 (같은 열쇠로 잠금/해제) |
| 대칭키 | 암호화/복호화에 사용하는 하나의 키 | 키 하나만 관리하면 되므로 구현이 단순하고 빠름 | 집 열쇠 하나로 문 잠그고 여는 느낌 |
| CBC 모드 | 암호화 동작 방식(블록 체인 방식) | 같은 데이터라도 IV만 바꾸면 암호문이 달라져 안전해짐 | 블록들이 서로 연결되며 암호화됨 |
| PKCS5Padding | 마지막 블록 채우기(패딩) | 문자열 길이가 애매해도 AES 블록 단위로 맞추기 위해 필요 | 빈 종이에 공백을 특정 규칙으로 채우는 느낌 |
| IV(초기 벡터) | AES/CBC에서 첫 블록을 섞는 값 | IV가 없으면 암호문 패턴이 노출됨. 보안 증가 | 자물쇠를 잠글 때 매번 다른 방식으로 비틀어주는 것 |
| Base64 | 바이너리 데이터를 문자열로 변환 | 암호문은 바이너리라 DB 저장, JSON 응답 등에 불편 → 문자열로 바꿔야 함 | 사진 파일을 문자로 바꾸는 것처럼 안전하게 표현 |
| SecretKeySpec | AES 키를 담는 자바 객체 | Cipher에서 사용할 수 있는 키 객체 형태로 변환 | “열쇠 재질을 규격에 맞게 가공한 형태” |
| IvParameterSpec | IV 값을 담는 자바 객체 | CBC 모드 초기화에 필요 | “문을 잠글 때 사용하는 초기 동작값” |
| Cipher | 암호화/복호화 수행 클래스 | 암호 알고리즘을 적용하는 실제 실행 엔진 | 자물쇠를 직접 잠그고 여는 도구 |
| AES-128/192/256 | AES의 키 길이 종류 | 길어질수록 보안 but 키 관리가 더 중요 | 열쇠 이빨 수가 많을수록 튼튼함 |
| Base64 인코딩 | 암호화된 바이트 → 문자열 | DB에 바로 바이트 저장하기 어려우니 문자열로 변환 | 바이너리를 문자처럼 보이게 하는 포장 |
2. 흐름
| 단계 | 설명 | 사용되는 개념 |
|---|---|---|
| 키 준비 | 설정파일(app.encrypt.key)에서 키 받음 | 대칭키, SecretKeySpec |
| IV 준비 | CBC에서 사용할 16바이트 IV 생성 | CBC, IV, IvParameterSpec |
| 암호화 준비 | Cipher 인스턴스 생성하고 ENCRYPT 모드로 설정 | Cipher |
| 파일명 암호화 | AES로 암호화 → 바이너리 암호문 생성 | AES, CBC, PKCS5Padding |
| DB 저장 | 바이너리를 Base64 문자열로 변환하고 저장 | Base64 |
| 복호화 과정 | Base64 → 바이너리 → AES 복호화 | Cipher, AES |
3. 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
@Service
public class AesFileNameEncryptionService implements FileNameEncryptionService {
// 암호화에 사용할 키 객체
private final SecretKeySpec keySpec;
// 사용할 암호화 알고리즘 설정
// "AES" + "CBC 모드" + "PKCS5Padding"
private final String algorithm = "AES/CBC/PKCS5Padding";
// CBC 모드에서 필요한 IV(초기 벡터)
private final IvParameterSpec iv;
// 생성자에서 설정 파일(application.yml)의 키값을 주입받음
public AesFileNameEncryptionService(@Value("${app.encrypt.key}") String key) {
// AES에서 필요한 키는 바이트 배열 형태
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
// AES 키 객체 생성 (32바이트면 AES-256)
this.keySpec = new SecretKeySpec(keyBytes, "AES");
// CBC 모드는 16바이트 IV가 필요
// 여기서는 키의 앞 16자리로 IV를 구성 (간단 버전)
// 실제 프로덕트에서는 랜덤 IV를 사용하는 것이 더 안전함
byte[] ivBytes = key.substring(0, 16).getBytes(StandardCharsets.UTF_8);
this.iv = new IvParameterSpec(ivBytes);
}
@Override
public String encrypt(String plain) {
try {
// Cipher 객체 생성 (AES/CBC/PKCS5Padding 알고리즘 사용)
Cipher cipher = Cipher.getInstance(algorithm);
// 암호화 모드로 초기화 (암호화 키 + IV 필요)
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
// 파일명(문자열)을 바이트 배열로 변환하여 암호화 수행
byte[] encrypted = cipher.doFinal(plain.getBytes(StandardCharsets.UTF_8));
// 암호화된 결과는 바이너리라 DB 저장 위해 Base64로 인코딩
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new RuntimeException("Encrypt error", e);
}
}
@Override
public String decrypt(String encrypted) {
try {
// Cipher 객체 생성
Cipher cipher = Cipher.getInstance(algorithm);
// 복호화 모드로 초기화
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
// Base64로 저장된 암호문을 원래 바이트 배열로 디코딩
byte[] decoded = Base64.getDecoder().decode(encrypted);
// 복호화 수행 → 평문(원래 파일명)
byte[] decrypted = cipher.doFinal(decoded);
return new String(decrypted, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("Decrypt error", e);
}
}
}
5. 플랜
- 파일업로드 할때 파일명을 암호화
- 파일명과 암호화한 데이터 테이블에 저장하고
- OS내에 저장
- if(사용시에 테이블내에 매핑된 이름이 있으면) { 복호화값으로 처리 },
- else{ 아니면 기존 이름 사용}
- 그러면 기존에 암호화되지 않은 파일도 사용이 가능하니까?
This post is licensed under CC BY 4.0 by the author.