본문 바로가기
💊 Java & Kotlin & Spring/- Java & kotlin

[Java] Bouncy Castle 라이브러리를 이용하여 파일 암호화, 복호화 하기 (feat.블록 암호의 SEED 알고리즘)

by Wonit 2021. 6. 19.

최근 BoB 면접을 준비하면서 지난 프로젝트들을 되돌아보고 있었다.

 

내 지난 프로젝트 중에 학과 연구실에서 학사 졸업 논문에 참여해서 파일 암복호화 모듈을 구현했던 경험이 있다.

 

현재 해당 프로그램의 소스 코드는 보안상 공개되어 있지 않고 나도 2년 전의 프로젝트라서 기억이 잘 나지 않았다.

 

그래서 최대한 그 기억을 더듬어 파일 암복호화 모듈을 다시 구현하려 하는데, 생각보다 한글로 된 자료가 많지 않아 다른 사람들에게 공유한다면 꽤 도움 될 것 같아서 그 과정을 한 번 포스팅하려 한다.

 


목차

  • Bouncy Castle
  • 블록 암호, SEED 알고리즘이란?
  • 프로젝트 생성
    • 프로젝트 생성
    • 의존성 추가
    • FileUtil.class 구현
    • BouncyModule.class 구현
    • Encryption.class 구현
  • 테스트

Bouncy Castle

우선 나는 파일 암호화 복호화를 위해 Bouncy Castle 라이브러리를 이용할 것이다.

 

Bouncy Castle은 유명한 암호화 라이브러리로 Java, C# 버전에 따라 배포된다.

 

대칭키 암호, 블록 암호, SEED 알고리즘이란?

 

우리는 Bouncy Castle 로 암호화를 수행할 것인데, 실질적으로 암호화를 수행할 알고리즘은 대칭키 암호 시스템을 이용할 것이다.

 

대칭키 암호는 암호화 키와 복호화 키가 동일한 암호 알고리즘이다.

 

대칭 키 암호 시스템에서는 2가지 방법이 대표적이다.

 

  1. 데이터를 블록 단위로 암호화 : 블록 암호
  2. 데이터를 스트림 단위로 암호화 : 스트림 암호

 

블록 암호란?

대표적인 블록 암호의 구조는 Feistal 과 SPN 구조가 존재하는데, 우리는 페이스탈 구조를 이용하려 한다.

 

페이스탈 구조는 라운드 함수가 반복적으로 적용되는 구조로 대표적으로는 DES와 국산 제품인 SEED가 존재한다.

 

참고로 SPN 구조는 가장 널리 사용되는 AES가 존재한다

 

우라는 블록 암호의 SEED 알고리즘을 사용하려 한다.

 

SEED 알고리즘이란?

 

SEED 알고리즘은 1999년 2월 한국인터넷진흥 원과 국내 암호전문가들이 순수 국내기술로 개발한 128비트 블록 암호 알고리즘이다.

 

1999년 개발되었는데 2005년 국제 표준화 기구인 ISO/IEC 국제 블록암호알고리즘 IETF표준으로 제정되었다.

 

이제 어느 정도 기본은 되었으니 프로젝트를 실제로 만들어보자!

 

프로젝트 생성

 

프로젝트는 Gradle 프로젝트로 생성하도록 하자!

 

 

그리고 Bouncy Castle 라이브러리 의존성을 build.gradle 에 추가해주자!

 

dependencies {
    implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.68'
}

 

그리고 아래 기술된 내용대로 잘 구현한다면 다음과 같은 디렉토리 구조를 갖게 될 것이다

 

 

FileUtil.java 구현

 

해당 클래스는 암호화할 파일을 불러오고, 암호화된 파일을 저장할 유틸 클래스이다.

 

public class FileUtil {

    private FileInputStream fileInputStream;
    private FileOutputStream fileOutputStream;

    public byte[] getFileBytesFrom(String path) throws IOException {
        // 파일 객체 생성
        File file = new File(path);
        fileInputStream = new FileInputStream(file);

        byte[] fileBytes = new byte[(int)file.length()];

        fileInputStream.read(fileBytes);

        fileInputStream.close();
        return fileBytes;
    }

    public void saveFile(String path, byte[] fileData) throws IOException {
        fileOutputStream = new FileOutputStream(path);

        fileOutputStream.write(fileData);

        fileOutputStream.close();
    }
}

 

BouncyModule.java 구현

 

이제 실제로 BouncyCastle 로 블록 암호를 이용해서 파일을 암호화 해보자

 

public class BouncyModule {

    public byte[] encrypt(String key, byte[] plainText) {
        byte[] keyBytes = key.getBytes();

        // 블록 암호 운용
        // 블록보다 데이터가 짧을 경우 패딩을 사용함
        // 블록 암호 알고리즘으로는 SEED 알고리즘을 사용함
        BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new SEEDEngine());
        // 초기화 및 키 파라미터 생성 첫 번째 매개변수가 true 라면 암호화 모드
        cipher.init(true, new KeyParameter(keyBytes));

        return getBytes(plainText, cipher);
    }

    public byte[] decrypt(String key, byte[] cipherText) {
        byte[] keyBytes = key.getBytes();

        BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new SEEDEngine());
        cipher.init(false, new KeyParameter(keyBytes));

        return getBytes(cipherText, cipher);
    }

    private byte[] getBytes(byte[] targetData, BufferedBlockCipher cipher) {
        byte[] outputData = new byte[cipher.getOutputSize(targetData.length)];

        int tam = cipher.processBytes(targetData, 0, targetData.length, outputData, 0);

        try {
            cipher.doFinal(outputData, tam);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return outputData;
    }
}

 

모드는 블록 암호 모드를 운용한다.

 

만약 블록보다 데이터의 길이가 짧을 경우 패딩을 사용하고, 블록 암호 알고리즘으로 SEED 알고리즘을 사용한다고 지정해준다.

 

EncryptUtil.java 구현

 

위의에서 생성한 파일 유틸과 bouncy castle 모듈을 이용해서 암호화 유틸을 구현해보자

 

public class EncryptUtil {
    private static BouncyModule bouncyModule = new BouncyModule();
    private static FileUtil fileUtil = new FileUtil();

    public static void encryption(String key, String filePath) throws IOException {
        byte[] plainFileBytes = fileUtil.getFileBytesFrom(filePath);
        byte[] encryptedFileBytes = bouncyModule.encrypt(key, plainFileBytes);
        fileUtil.saveFile(filePath, encryptedFileBytes);
    }

    public static void decryption(String key, String filePath) throws IOException {
        byte[] encryptedFileBytes = fileUtil.getFileBytesFrom(filePath);
        byte[] decryptedFile = bouncyModule.decrypt(key, encryptedFileBytes);
        fileUtil.saveFile(filePath, decryptedFile);
    }
}

테스트

 

이제 구현이 끝났으니 파일 하나를 잡고 테스트해보자

 

나는 바탕화면에 있는 학교 로고 png 파일을 암호화 해보도록 하겠다!

 

 

암호화를 하기 전에는 파일이 잘 열린다.

 

이제 main 함수에서 암호화를 수행하자

 

public class Main {
    public static void main(String[] args) throws Exception {

        String key = "$2a$10$mxLAM1fAEDPWkFz83IPDH.7yvyY6njmIMSk937jdH9UD3HhbqYPgO";
        String path = "/Users/my/Desktop/logo.png";

        EncryptUtil.encryption(key, path);
    }
}

 

그럼 다음과 같이 파일을 열 수 없다고 나온다

 

 

그리고 복호화를 수행해보자

 

public class Main {
    public static void main(String[] args) throws Exception {

        // key 생성 (60 길이)
        String key = "$2a$10$mxLAM1fAEDPWkFz83IPDH.7yvyY6njmIMSk937jdH9UD3HhbqYPgO";
        String path = "/Users/jangwonik/Desktop/logo.png";

        // EncryptUtil.encryption(key, path);
        EncryptUtil.decryption(key, path);
    }
}

 

그럼 다음과 같이 다시 파일을 열 수 있게 되었다!

 

 

댓글0