Post

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 응답 등에 불편 → 문자열로 바꿔야 함사진 파일을 문자로 바꾸는 것처럼 안전하게 표현
SecretKeySpecAES 키를 담는 자바 객체Cipher에서 사용할 수 있는 키 객체 형태로 변환“열쇠 재질을 규격에 맞게 가공한 형태”
IvParameterSpecIV 값을 담는 자바 객체CBC 모드 초기화에 필요“문을 잠글 때 사용하는 초기 동작값”
Cipher암호화/복호화 수행 클래스암호 알고리즘을 적용하는 실제 실행 엔진자물쇠를 직접 잠그고 여는 도구
AES-128/192/256AES의 키 길이 종류길어질수록 보안 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.